在高級API編程時,你不能控制顯示在屏幕上的內容,甚至用編程方式幾乎無法控制這些組件。由系統實現體負責選取設備的最佳顯示方式。然而,一些如游戲類的應用程序可能需要對屏幕繪制有更多的控制。MIDP javax.microedition.lcdui包也提供了處理這類編程的低級API。
為了在屏幕直接繪制直線,文本和形狀,你必須使用Canvas類。該類提供了一個MIDlet可以在其上繪制的空白屏幕。例如,在屏幕上繪制字符串"HelloWorld"。實現這個功能有一個簡單的辦法:子類化Canvas類(它是繼承自Displayable的一個抽象類)並重載paint()方法。詳見代碼段1。
paint()方法的實現使用了javax.microedition.lcdui.Graphics類的繪圖功能。在方法paint()中,繪圖顏色置為紅色,然後用紅色畫一個長方形。其中,方法getWidth( )和getHeight( )分別返回Canvas對象的寬度和高度。接下來setColor( )方法把繪圖顏色設置為白色;之後,字符串"Hello World!"繪制在屏幕的左上角。
示例1:子類化Canvas
import javax.microedition.lcdui.*;
public class MyCanvas extends Canvas {
public void paint(Graphics g) {
g.setColor(255, 0, 0);
g.fillRect(0, 0, getWidth(), getHeight( ));
g.setColor(255, 255, 255);
g.drawString("Hello World!", 0, 0, g.TOP | g.LEFT);
}
}
現在,為了觀看MyCanvas,必須要把實例化後進行顯示。既然Canvas是Displayable的一個子類,可以用與其它screen 類使用的同樣的setCurrent( )方法來顯示它。詳見代碼段2。
示例2:實例化和顯示MyCanvas
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class MyMidlet extends MIDlet {
public MyMidlet( ) {}
public void startApp( ) {
Canvas canvas = new MyCanvas( );
Display display = Display.getDisplay(this);
display.setCurrent(canvas);
}
public void pauseApp( ) {}
public void destroyApp(boolean unconditional) {}
}
如果你在無線開發包提供的模擬器中運行,可能得到如圖1所示的效果。注意在代碼段5-1中,顏色被設置為紅色與白色,但是既然使用了灰度級屏幕,這裡的彩色就被映射到黑色和白色的不同的灰度級上。試著調整顯示來觀察哪一種設備的色彩顯示更好些。
圖1.在Canvas上繪制"Hello World!"
1. 繪圖
坐標(0,0)代表了顯示屏的左上角。X坐標的值是從左向右遞增的,Y坐標的值是從頂向下遞增的。應用程序應該一直檢查繪圖區的大小,這可以通過如下的類Canvas提供的方法實現:
public int getHeight( );
public int getWidth( );
這兩個方法分別以像素值返回可顯示區的高度和寬度。
所用的繪圖模型是像素替換法--用在graphics對象中指定的當前像素值來代替目標像素值。一個24位的色彩模型由紅,綠,藍各8位(RGB)來提供。然而,由於並非所有的設備都支持彩色,應用程序中要求的顏色將被映射到設備中可用的顏色值上。但是,一個良好的應用程序,應該檢查一個設備是否支持彩色-這可以用Display類的方法isColor()和numColors( )來完成。
Graphics類提供了方法setColor()和getColor( )來設置和取得顏色。但是,不象AWT/Swing,不存在方法setBackground( )和setForeground(),所以你必須顯式地調用fillRect( )(見示例5-1)。Graphics類的大多數的另外一些方法是自解釋的,與該類的AWT版本中的方法差不多。但是,我們還是要仔細地看一下其中的幾個在環境J2ME下是如何工作的。
2. 雙緩沖
雙緩沖機制經常用來實現動畫的平滑顯示效果。在這種技術中,你不是在顯示屏上繪圖,而是繪制到顯示屏的一個拷貝上(一個脫屏的緩沖區)-它是內存的一部分。當緩沖區繪制結束,你就可以把緩沖區內容拷貝到顯示屏上。這裡的基本原理是,直接拷貝內存內容到顯示屏上要快於用原型繪制的速度。
要實現雙緩沖,可以先生成一個其尺寸與屏幕一樣的易變圖像:
int width = getWidth( );
int height = getHeight( );
Image buffer = Image.createImage(width, height);
然後,取得緩沖區的圖形上下文:
Graphics gc = buffer.getGraphics( );
現在,你可以在緩沖區中繪圖了:
// animate
// ..
gc.drawRect(20, 20, 25, 30);
當需要把緩沖區拷貝到屏幕上時,你可以重載方法paint( )來把緩沖區內容繪制到設備顯示屏上:
public void paint(Graphics g) {
g.drawImage(buffer, 0, 0, 0);
}
注意,一些MIDP實現中已經采用了雙緩沖機制,因此這裡的工作可能是不必要的。你可以使用Canvas的isDoubleBuffered( )方法來檢查是否圖形是雙緩沖方式實現的。
3. 線程問題
MIDP GUI API是線程安全的。即是說,GUI API方法可以在任何時候從任何線程中調用。唯一的例外是Canvas類的serviceRepaints( )方法,它立即調用了paint( )方法以強迫顯示的立即重繪。這是說,如果paint( )方法想在任何的應用程序調用serviceRepaints( )時已鎖定的對象上同步,應用程序將會產生死鎖。為了避免死鎖,在使用了serviceRepaints( )方法時,請不要鎖定將被paint()方法使用的對象。
另外,在所有未執行的重繪滿足後,你可以使用Display類的方法callSerially( )來執行代碼,如下所示:
class TestCanvas extends Canvas implements Runnable {
void doSomething( ) {
//代碼段1
callSerially(this);
}
public void run( ) {
//代碼段2
}
}
在此,對象的run( )方法在初始化結束後被調用。
4. 字體
應用程序不能自己產生字體。代之的是,應用程序應該要求基於一些屬性(如大小,字體名稱,字形)的一種字體,底層系統將試著返回一種與要求的字體最相近的字體。Font 類用來描述各種字體和尺寸。在Font 類中一共定義了三種字體屬性,每一種屬性取值不同,如下:
屬性 取值 Face MONOSPACE, PROPORTIONAL, SYSTEM Size SMALL, MEDIUM, LARGE Style BOLD, ITALIC, PLAIN, UNDERLINED例如,要指定一種中等大小的字體,可以使用Font.SIZE_MEDIUM;用Font.STYLE_ITALIC來指定傾斜字形,等等。字形屬性值可以用OR(|)操作符結合;另外一些屬性值不能結合。例如:下面這種屬性值指定了一種常規,帶下劃線的字體:
STYLE_PLAIN | STYLE_UNDERLINED
而,下面是非法的組合:
SIZE_SMALL | SIZE_MEDIUM
下面也是非法的:
FACE_SYSTEM | FACE_MONOSPACE
系統中的每種字體實際上都是分別實現的,所以為了取得描述字體的對象,可以用getFont( )方法--該方法有三個參數,分別對應字體的字面,大小和字形。如,下面的代碼以指定的字面,大小和字形屬性得到一個Font對象:
Font font = Font.getFont(FACE_SYSTEM,STYLE_PLAIN, SIZE_MEDIUM);
如果沒有相匹配的字體,系統將盡可能提供最相近的匹配-總是一個有效的字體對象。
一旦得到一種字體,你就可以使用Font類的方法來檢索這種字體的信息。如,你可以用getFace(),getSize( )和getStyle( )方法來分別檢索該字體的字面,大小和字形信息。
讓我們再看一個例子:示例3中代碼子類化Canvas 類。在此,繪圖顏色設置為白色,並用該色畫出一個矩形,然後把繪圖色置為黑色。代碼剩下的部分在設備屏幕上繪制系統字體,如圖2所示。
圖2.在設備顯示屏上畫出系統字體
示例3:使用字體
import javax.microedition.lcdui.*;
public class FontCanvas extends Canvas {
public void paint(Graphics g) {
g.setColor(0xffffff);
g.fillRect(0, 0, getWidth(), getHeight( ));
g.setColor(0x000000);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,Font.SIZE_LARGE));
g.drawString("System Font", 0, 0, g.LEFT | g.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN,Font.SIZE_MEDIUM));
g.drawString("Medium Size", 0, 15, g.LEFT | g.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD,Font.SIZE_MEDIUM));
g.drawString("Bold Style", 0, 30, g.LEFT | g.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_ITALIC,Font.SIZE_MEDIUM));
g.drawString("Italic Style", 0, 45, g.LEFT | g.TOP);
g.setFont(Font.getFont(Font.FACE_SYSTEM,Font.STYLE_UNDERLINED, Font.SIZE_MEDIUM));
g.drawString("Underlined Style", 0, 60, g.LEFT | g.TOP);
}
}
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class FontMidlet extends MIDlet {
public FontMidlet( ) {}
public void startApp( ) {
Canvas canvas = new FontCanvas( );
Display display = Display.getDisplay(this);
display.setCurrent(canvas);
}
public void pauseApp( ) {}
public void destroyApp(boolean unconditional) {}
}