Trong phần trước, chúng ta đã tạo lớp clsFont để vẽ chữ bằng bitmap font. Bài học hôm nay sẽ sử dụng class này để xuất chữ lên màn hình.
Sử dụng Bitmap Font
Hãy mở code lớp clsCanvas để bắt đầu. Chúng ta sẽ thêm biến toàn cục myFont làm đối tượng lớp clsFont.
private midMain fParent;
private clsFont myFont;
/** Creates a new instance of clsCanvas */
public clsCanvas(midMain m) {
Nếu bạn mở folder images ra sẽ thấy ảnh fonts.png trong đó. Đấy chính là font chúng ta sử dụng trong phương thức clsFont.load( ). Thêm 2 dòng sau vào cuối phương thức load( ) của lớp clsCanvas.
}
myFont = new clsFont();
myFont.load("/images/fonts.png");
}
Thêm lời gọi phương thức unload của clsFont vào cuối phương thức unload( ) của clsCanvas
public void unload(){
// make sure the object get's destroyed
myFont.unload();
myFont = null;
}
Cuối cùng ta dùng phương thức drawString( ) của clsFont để vẽ chữ lên màn hình. Thêm các dòng sau trước lời gọi phương thức flushGraphics( ) trong vòng lặp chính:
g.fillRect(0, 0, screenW, screenH);
myFont.drawString(g, "Hello Neo...", 10, 50);
myFont.drawString(g, "1234567890", 10, 70);
myFont.drawString(g, "ABCDEFG abcdefg", 10, 90);
flushGraphics();
Sau đó chúng ta test những gì vừa làm được:
package MyGame; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; import javax.microedition.lcdui.game.GameCanvas; public class clsCanvas extends GameCanvas implements Runnable { // key repeat rate in milliseconds public static final int keyDelay = 250; //key constants public static final int upKey = 0; public static final int leftKey = 1; public static final int downKey = 2; public static final int rightKey = 3; public static final int fireKey = 4; //key states for up, left, down, right, and fire key private boolean[] isDown = { false, false, false, false, false }; //last time the key changed state private long[] keyTick = { 0, 0, 0, 0, 0 }; //lookup table for key constants :P private int[] keyValue = { GameCanvas.UP_PRESSED, GameCanvas.LEFT_PRESSED, GameCanvas.DOWN_PRESSED, GameCanvas.RIGHT_PRESSED, GameCanvas.FIRE_PRESSED }; private boolean isRunning = true; private Graphics g; private midMain fParent; private clsFont myFont; /** Creates a new instance of clsCanvas */ public clsCanvas(midMain m) { super(true); fParent = m; setFullScreenMode(true); } public void start(){ Thread runner = new Thread(this); runner.start(); } public void load(){ try{ // load the images here }catch(Exception ex){ // exit the app if it fails to load the image isRunning = false; return; } myFont = new clsFont(); myFont.load("/images/fonts.png"); } public void unload(){ // make sure the object gets destroyed myFont.unload(); myFont = null; } public void checkKeys(int iKey, long currTick){ long elapsedTick = 0; //loop through the keys for (int i = 0; i < 5; i++){ // by default, key not pressed by user isDown[i] = false; // is user pressing the key if ((iKey & keyValue[i]) != 0){ elapsedTick = currTick - keyTick[i]; //is it time to toggle key state? if (elapsedTick >= keyDelay){ // save the current time keyTick[i] = currTick; // toggle the state to down or pressed isDown[i] = true; } } } } public void run() { int iKey = 0; int screenW = getWidth(); int screenH = getHeight(); long lCurrTick = 0; // current system time in milliseconds; load(); g = getGraphics(); while(isRunning){ lCurrTick = System.currentTimeMillis(); iKey = getKeyStates(); checkKeys(iKey, lCurrTick); if (isDown[fireKey]){ isRunning = false; } //restore the clipping rectangle to full screen g.setClip(0, 0, screenW, screenH); //set drawing color to black g.setColor(0x000000); //fill the screen with blackness g.fillRect(0, 0, screenW, screenH); myFont.drawString(g, "Hello Neo...", 10, 50); myFont.drawString(g, "1234567890", 10, 70); myFont.drawString(g, "ABCDEFG abcdefg", 10, 90); flushGraphics(); try{ Thread.sleep(30); } catch (Exception ex){ } } g = null; unload(); fParent.destroyApp(false); fParent = null; } }
Xem xét vấn đề
Bạn cũng có thể xem xét thêm phương thức wrappers trong lớp clsFont để vẽ kiểu int như sau:
Một biến thể của phương thức drawString( ) vẽ ra chuỗi với lề bên phải có thể được tạo một cách dễ dàng với đoạn code sau:
Với phương thức vẽ kiểu int:
Những phương thức vừa thêm có thể dùng để vẽ điểm số và chữ số căn lề bên phải.
Bitmap Font với chức năng Word Wrap
public void drawInt(Graphics g, int num, int x, int y){ drawString(g, Integer.toString(num), x, y); } public void drawLong(Graphics g, long num, int x, int y){ drawString(g, Long.toString(num), x, y); }
Một biến thể của phương thức drawString( ) vẽ ra chuỗi với lề bên phải có thể được tạo một cách dễ dàng với đoạn code sau:
//draws string from right to left starting at x,y
public void drawStringRev(Graphics g, String sTxt, int x, int y){
// get the strings length
int len = sTxt.length();
// set the starting position
int cx = x;
// if nothing to draw return
if (len == 0) {
return;
}
// our fail-safe
if (useDefault){
g.drawString(sTxt, x, y, Graphics.TOP | Graphics.RIGHT);
return;
}
// loop through all the characters in the string
for (int i = (len - 1); i >= 0; i--){
// get current character
char c = sTxt.charAt(i);
// get ordinal value or ASCII equivalent
int cIndex = (int)c;
// lookup the width of the character
int w = charW[cIndex];
// go to the next drawing position
cx -= (w + charS);
// draw the character
drawChar(g, cIndex, cx, y, w, charH);
}
}
Với phương thức vẽ kiểu int:
public void drawIntRev(Graphics g, int num, int x, int y){
drawStringRev(g, Integer.toString(num), x, y);
}
public void drawLongRev(Graphics g, long num, int x, int y){
drawStringRev(g, Long.toString(num), x, y);
}
Những phương thức vừa thêm có thể dùng để vẽ điểm số và chữ số căn lề bên phải.
Bitmap Font với chức năng Word Wrap
Sau đây là các phương thức bổ sung để bạn có thể dùng chức năng word wrap với bitmap font
// space between lines in pixels public int lineS = 2; // draws words that wrap between x and x1 public void drawStringWrap(Graphics g, String s, int x, int y, int x1){ int len = s.length(); // current x int tx = x; // current y int ty = y; /* word buffer contents width - I just thought it would be faster than calling the String.length() method */ int ww = 0; // word buffer String sWord = ""; for (int i = 0; i < len; i++){ char c = s.charAt(i); int cIndex = (int)c; int cw = charW[cIndex]; if ((cIndex > 32) && (cIndex < 127)){ //if not a space and the character is printable //add the character to the buffer sWord += String.valueOf(c); //compute the length of the current word ww += cw; } else { //if space or non-printable character // check if there is a word in the buffer if (ww > 0) { //check if the word goes past the right margin if ((tx + ww) > x1){ // carrage return tx = x; // line feed ty += (charH + lineS); } // draw the contents of the word buffer drawString(g, sWord, tx, ty); } //move to the next position tx += (ww + cw); // clear the word buffer sWord = ""; // word buffer width to zero ww = 0; } } // if there is a word remaining in the buffer then draw it if (ww > 0) { if ((tx + ww) > x1){ tx = x; ty += (charH + lineS); } drawString(g, sWord, tx, ty); } }
- Graphics g - đối tượng Graphics dùng vẽ font
- String s - chuỗi cần vẽ
- int x - lề trái và tọa độ x bắt đầu vẽ
- int y - tọa độ y dòng đầu tiên
- int x1 - lề phải để chữ tự động xuống dòng
Phương thức drawStringWrap( ) sử dụng phương thức drawString( ) để vẽ các từ. Phương thức này xem "các kí tự không hiển thị được và dấu cách" là một "dấu phân cách giữa các từ". Bạn có thể thay đổi khoảng cách các dòng với nhau thông qua giá trị biến lineS.
Để thử nghiệm, bạn thêm đoạn code sau vào trước lời gọi flushGraphics( )
myFont.drawString(g, "ABCDEFG abcdefg", 10, 90);
myFont.drawStringWrap(g, "blah blah blah blah blah blah " +
"blah blah blah blah blah blah blah blah blah " +
"blah blah blah blah blah blah blah blah blah " +
"blah blah blah blah blah blah blah blah blah " +
"blah blah blah blah blah blah blah blah blah " +
"blah ", 10, 100, 166);
flushGraphics();
Bạn sẽ thấy kết quả thế này
Sau đây là code hoàn chỉnh lớp clsFont sau khi thêm các phương thức mới
package MyGame; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.Image; public class clsFont { // additional space between characters public int charS = 0; // max clipping area public int screenW = 176; public int screenH = 208; // flag: set to true to use the Graphics.drawString() method // this is just used as a fail-safe public boolean useDefault = false; // height of characters public int charH = 10; // lookup table for character widths public int[] charW = { // first 32 characters 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // space 9, // everything else XD 3, 5, 8, 8, 7, 8, 3, 5, 5, 6, 7, 3, 7, 3, 9, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 6, 6, 6, 6, 9, 6, 6, 6, 6, 6, 6, 6, 6, 3, 6, 6, 6, 9, 6, 6, 6, 6, 6, 6, 7, 6, 6, 9, 6, 6, 6, 5, 9, 5, 4, 6, 4, 6, 6, 6, 6, 6, 6, 6, 6, 3, 4, 6, 3, 9, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 6, 6, 6, 5, 3, 5, 4, // delete 9}; // the bitmap font image private Image imgFont; public clsFont() { } public boolean load(String imagePath){ useDefault = false; try{ // load the bitmap font if (imgFont != null){ imgFont = null; } imgFont = Image.createImage(imagePath); } catch (Exception ex){ // oohh we got an error then use the fail-safe useDefault = true; } return (!useDefault); } public void unload(){ // make sure the object gets destroyed imgFont = null; } public void drawChar(Graphics g, int cIndex, int x, int y, int w, int h){ // non printable characters don't need to be drawn if (cIndex < 33){ return; } // neither does the delete character if (cIndex > 126){ return; } // get the characters position int cx = cIndex * 9; // reset the clipping rectangle g.setClip(0, 0, screenW, screenH); // resize and reposition the clipping rectangle // to where the character must be drawn g.clipRect(x, y, w, h); // draw the character inside the clipping rectangle g.drawImage(imgFont, x - cx, y, Graphics.TOP | Graphics.LEFT); } public void drawString(Graphics g, String sTxt, int x, int y){ // get the strings length int len = sTxt.length(); // set the starting position int cx = x; // if nothing to draw return if (len == 0) { return; } // our fail-safe if (useDefault){ g.drawString(sTxt, x, y, Graphics.TOP | Graphics.LEFT); return; } // loop through all the characters in the string for (int i = 0; i < len; i++){ // get current character char c = sTxt.charAt(i); // get ordinal value or ASCII equivalent int cIndex = (int)c; // lookup the width of the character int w = charW[cIndex]; // draw the character drawChar(g, cIndex, cx, y, w, charH); // go to the next drawing position cx += (w + charS); } } // extended methods *************************************** public void drawInt(Graphics g, int num, int x, int y){ drawString(g, Integer.toString(num), x, y); } public void drawLong(Graphics g, long num, int x, int y){ drawString(g, Long.toString(num), x, y); } // Right align methods **************************************** //draws string from right to left starting at x,y public void drawStringRev(Graphics g, String sTxt, int x, int y){ // get the strings length int len = sTxt.length(); // set the starting position int cx = x; // if nothing to draw return if (len == 0) { return; } // our fail-safe if (useDefault){ g.drawString(sTxt, x, y, Graphics.TOP | Graphics.RIGHT); return; } // loop through all the characters in the string for (int i = (len - 1); i >= 0; i--){ // get current character char c = sTxt.charAt(i); // get ordinal value or ASCII equivalent int cIndex = (int)c; // lookup the width of the character int w = charW[cIndex]; // go to the next drawing position cx -= (w + charS); // draw the character drawChar(g, cIndex, cx, y, w, charH); } } public void drawIntRev(Graphics g, int num, int x, int y){ drawString(g, Integer.toString(num), x, y); } public void drawLongRev(Graphics g, long num, int x, int y){ drawString(g, Long.toString(num), x, y); } // Word wrap method **************************************** // space between lines in pixels public int lineS = 2; // draws words that wrap between x and x1 public void drawStringWrap(Graphics g, String s, int x, int y, int x1){ int len = s.length(); // current x int tx = x; // current y int ty = y; /* word buffer contents width - I just thought it would be faster than calling the String.length() method */ int ww = 0; // word buffer String sWord = ""; for (int i = 0; i < len; i++){ char c = s.charAt(i); int cIndex = (int)c; int cw = charW[cIndex]; if ((cIndex > 32) && (cIndex < 127)){ //if not a space and the character is printable //add the character to the buffer sWord += String.valueOf(c); //compute the length of the current word ww += cw; } else { //if space or non-printable character // check if there is a word in the buffer if (ww > 0) { //check if it goes past the right margin if ((tx + ww) > x1){ // carrage return tx = x; // line feed ty += (charH + lineS); } // draw the contents of the word buffer drawString(g, sWord, tx, ty); } //move to the next position tx += (ww + cw); // clear the word buffer sWord = ""; // word buffer width to zero ww = 0; } } // if there is a word remaining in the buffer then draw it if (ww > 0) { if ((tx + ww) > x1){ tx = x; ty += (charH + lineS); } drawString(g, sWord, tx, ty); } } }
Còn rất nhiều thứ có thể làm để thêm chức năng cho clsFont. Ví dụ như căn chỉnh chữ, tối ưu các code trên hay thêm các font chữ khác và viết code cho người chơi lựa chọn loại font họ thích...
Chúc các bạn thành công !!
Không có nhận xét nào:
Đăng nhận xét