[J2ME] MIDP低級界面開發(fā)
MIDP低級界面開發(fā)<轉(zhuǎn)載>
MIDP低級界面開發(fā)——使用LCDUI低級API
高級API使用簡單、有很高的可移植性,卻無法控制許多細節(jié)。要對界面更多的進行控制,必須使用低級API。
5.1 Canvas類開發(fā)簡介
低級界面屏幕都繼承自名為Canvas的屏幕類。Canvas類提供了一系列鍵盤低級事件和繪圖接口,具體的繪圖操作則由一個名為Graphics的圖形類來完成。
5.1.1 Canvas類簡介
Canvas即畫布,可以在其上繪制不同圖案。Canvas提供了一個繪圖接口方法paint(Graphics g),凡是繼承Canvas的繼承類都必須實現(xiàn)paint()方法,因此可以在paint方法中實現(xiàn)屏幕的繪畫代碼。
示例:
class MyCanvas extends Canvas
{
public void paint(Graphics g)
{
…
}
}
paint方法傳入了Grpahics類型的參數(shù)g,具體的繪畫將由Graphics的g實現(xiàn)。可以看出Canvas類和Graphics類的關(guān)系是畫布與畫筆的關(guān)系。
5.1.2 低級API與低級事件
Canvas可以處理低級事件,但并非處理所有的系統(tǒng)事件。設(shè)備支持哪些系統(tǒng)事件,必須由硬件的支持程度來判斷。Canvas提供一些方法判斷硬件支持程度。
功能
檢測方法
低級事件/回調(diào)函數(shù)
鍵盤事件
支持
keyPressed(int keycode)
keyReleased(int keycode)
屏幕事件
支持
showNotity()
hideNotify()
重繪事件
支持
paint(Graphics g)
是否支持雙緩沖
Canvas.isDoubleBuffered()
無
是否支持repeat
Canvas.hasRepeatdEvent()
keyRepeated(int keycode)
是否支持觸控屏幕事件
Canvas.hasPointerEvents()
pointerPressed(int x, int y)
pointerReleased(int x, int y)
是否支持觸控屏幕拖拽事件
Canvas.hasPointerMotionEvnets()
pointerDragged(int x, int y)
機器一定會支持的鍵盤事件有keyPressed()、keyReleased(),屏幕事件showNotify()、hideNotidy(),以及重繪事件paint()。
注意:除了showNotidy()之外,其它回調(diào)函數(shù)只有在此Canvas是目前屏幕上的畫面時才會被調(diào)用。
5.1.3 重繪事件
示例:
//CanvasTestMidlet.java
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class CanvasTestMidlet extends MIDlet
{
private Display display;
public CanvasTestMidlet()
{
display = Display.getDisplay(this);
}
public void startApp()
{
MyCanvas mc = new MyCanvas();
display.setCurrent(mc);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}
//MyCanvas.java
import javax.microedition.lcdui.*;
public class MyCanvas extends Canvas
{
/** Creates a new instance of MyCanvas */
public MyCanvas()
{
}
public void paint(Graphics g)
{
}
}
任何時候都可以自行調(diào)用repaint()產(chǎn)生重繪事件。repaint()有兩個同名方法,其中一個需要四個參數(shù),用來指定重畫區(qū)域的X、Y坐標(biāo),寬度與高度;另外一個無參數(shù),代表重繪整個屏幕。調(diào)用repaint()之后會立刻返回,繼續(xù)下面工作,調(diào)用paint()回調(diào)函數(shù)的工作則由一個專門處理UI的線程來完成。若希望等到paint()完成后再返回,可以在repaint()之后立刻調(diào)用serviceRepaints()方法。
注意:serviceRepaints()用來強制隊列中的重繪事件快點做完,如果隊列中沒有重繪事件,則serviceRepaints()什么也不會做,因此在調(diào)用serviceRepaints()之前,通常伴隨一個repaint()。
5.1.4 坐標(biāo)系統(tǒng)
在使用繪圖函式前,請先注意MIDP 中X 坐標(biāo)與Y 坐標(biāo)的定義方式。傳統(tǒng)的笛卡爾坐標(biāo)其原點在左下角,向右X 坐標(biāo)值遞增,向上Y坐標(biāo)值遞增。
但是我們在手機的屏幕上做圖時,變成向右X 坐標(biāo)值遞增,向下Y 坐標(biāo)值遞增。
5.1.5 像素(Pixel)
我們在所有圖形相關(guān)函數(shù)之中所使用的坐標(biāo)所代表的并非像素本身,而是指像素和像素之間的空格所構(gòu)成的坐標(biāo),如下圖所示:
像素與像素之間所構(gòu)成的坐標(biāo)
所以一般我們所說的坐標(biāo)(3,1)并非指位于(3,1)這個像素,而是指像素(2,0)、(2,1)、(3,0)、(3,1)所包圍的這個部分。也正因為坐標(biāo)指的并非圖素本身,所以會造成在繪制圖型和填滿區(qū)塊時有所差異,這兩者的不同我們將在以后說明。
5.1.6 Graphics入門
paint(Graphics g)方法會傳入一個Graphics對象作為參數(shù),可以把該對象當(dāng)作是一個抽象的畫筆,調(diào)用Graphics的方法,就可以在這個畫布上繪圖。
編寫我們自己的Canvas時,要做的第一件事就是把畫面清空,然后才開始繪圖,避免畫面上殘留前一個畫面所遺留下的東西。
//清屏
public void paint(Graphics g)
{
g.setColor(255, 0, 255);
g.fillRect(0, 0, getWidth(), getHeight());
… …
}
上述范例中,我們使用Graphics的setColor()來設(shè)置畫筆顏色:
setColor(int r, int g, int b)注意,rgb的值限定在0~255之間。
或setColor(int rgb) 直接傳入0x00RRGGBB這樣的整數(shù)。
設(shè)定好顏色后,可以使用getRedComponent()、getGreenComponent()、getBlueComponent()分別取得R、G、B的顏色設(shè)定。或者直接使用getColor()取得0x00RRGGBB這樣的整數(shù),也就是說,最后第0~7 位代表藍色、8~15 代表藍色,16~23 代表紅色。
getDisplayColor()較特殊,返回機器上繪圖時真正使用的顏色。有些機器不具有顯示所有顏色的能力。屏幕的灰度數(shù)可以用getGrayScale()取得,也可以用setGrayScale(),灰度數(shù)取值在0~255之間。使用這個函式的時候請?zhí)貏e注意,如果您已經(jīng)使用了相對應(yīng)的setGrayScale()來設(shè)定灰階色階數(shù),那么getGrayScale()函式只是單純地傳回設(shè)定值。但是如果您沒有設(shè)定灰階色階數(shù),那么呼叫g(shù)etGrayScale()函式的時候,會導(dǎo)致系統(tǒng)利用目前作用色的R、G、B 值來做大量運算,并求得最接近的灰階色階數(shù)。
5.1.7 繪制直線
我們可以使用Graphics 類別的drawLine()函式繪制線段。DrawLine 的四個參數(shù)分別是起點X 坐標(biāo),起點Y 坐標(biāo)、終點X 坐標(biāo)、終點Y 坐標(biāo)。舉例來說,如果我們函式呼叫為:g.drawLine(1,1,1,6)
則實際繪制的線段如下圖所示:
實際繪制出來的線段的位置
我們可以發(fā)現(xiàn)坐標(biāo)右邊的相素都會被填滿。
如果我們函式調(diào)用為:
g.drawLine(1,1,6,1)
則實際繪制的線段如下圖所示:
實際繪制出來的線段的位置
我們可以發(fā)現(xiàn)坐標(biāo)下方的像素都會被填滿。
當(dāng)我們繪圖形時,有所謂的筆觸(stroke style)。Graphics提供兩種筆觸,分別是Graphics.SOLID和Graphics.DOTTED:
g.setStrokeStyle(Graphics.DOTTED);
相應(yīng)的取得目前所用筆觸:g.getStrockStyle()
5.1.8 畫弧形
我們可以使用Graphics 類的drawArc()方法繪制弧形。drawArc 共有6 個參數(shù),它們分別是:前四個決定弧形所在的矩形范圍,第五個決定起始角度,第六個參數(shù)則決定弧形本身所涵蓋的角度。
如果我們方法調(diào)用為:
g.drawArc(20,10,width,height,45,90);
則實際繪制的弧形如下圖所示:
實際繪制出來的弧形
填充弧形
我們可以使用Graphics 類別的fillArc()函式填充弧形。fillArc 共有6 個參數(shù),它們分別是:前四個決定弧形所在的矩形范圍,第五個決定起始角度,第六個參數(shù)則決定弧形本身所涵蓋的角度。
如果我們方法調(diào)用為:
g.fillArc(20,15,width,height,45,90);
則實際繪制的填充弧形如下圖所示:
實際繪制出來的填充弧形
5.1.9 矩形
畫矩形:我們可以使用Graphics 類別的drawRect()函式繪制矩形。drawRect 有4 個參數(shù),分別是起點X 坐標(biāo)、起點Y 坐標(biāo)、寬度、長度。
如果我們函數(shù)調(diào)用為:
g.drawRect(1,1,6,8)
則實際繪制的矩形如下圖所示:
實際繪制出來的矩形
我們可以發(fā)現(xiàn)所構(gòu)成的矩形路徑,其右邊和下方的像素都被填滿了。
畫圓角矩形:我們可以使用Graphics 類別的drawRoundRect()函式繪制圓角矩形。其實drawRoundRect()和drawRect()函式的前四個參數(shù)意義相同,唯一的差距只有在最后兩個參數(shù),它們分別是圓角所在矩形的寬度,以及圓角所在矩形的高度。
如果我們方法調(diào)用為:
g.drawRoundRect(1,1,6,8,arcWidth,arcHeight)
則實際繪制的圓角矩形,在矩形的部分和使用drawRect()的結(jié)果相同,差別只有在四個直角的樣子不再是直角,而變成圓角。
如下圖所示:
實際繪制出來的圓角矩形
填充矩形:我們可以使用Graphics 類別的fillRect()函式填充矩形。fillRect 有4 個參數(shù),分別是起點X 坐標(biāo)、起點Y 坐標(biāo)、寬度、長度。
如果我們方法調(diào)用為:
g.fillRect(1,1,6,8)
則實際繪制的矩形如下圖所示:
實際繪制出來的填充矩形
我們可以發(fā)現(xiàn)只有包含在矩形路經(jīng)之內(nèi)的圖素才會被填滿,這和drawRect()函式的結(jié)果有所不同(上下都差一個圖素的大小)。
填充圓角矩形:我們可以使用Graphics 類別的fillRoundRect()函式填充圓角矩形。其實fillRoundRect()和fillRect()函式的前四個參數(shù)意義相同,唯一的差距只有在最后兩個參數(shù),它們分別是圓角所在舉行的寬度,以及圓角所在矩形的高度。
如果我們方法調(diào)用為:
g.fillRoundRect(1,1,6,8,arcWidth,arcHeight)
則實際繪制的圓角矩形,在矩形的部分和使用fillRect()的結(jié)果相同,差別只有在四個角的樣子不再是直角,而變成圓角,如下圖所示:
實際繪制出來的填充圓角矩形
5.1.10 三角形
繪制三角形,使用三個頂點,分別畫線即可,因此MIDP只提供填充三角形的功能:
g.fillTriangle(int x1, int y1, int x2, int y2, int x3, int y3) ;
練習(xí):嘗試自己繪制三角形,并填充它
5.2 Canvas與屏幕事件處理
Canvas本身具有兩種狀態(tài):
?? 普通狀態(tài)
?? 全屏狀態(tài)
可以使用setFullScreenMode()設(shè)定Canvas狀態(tài)。
示例:
/*
* FullScreenCanvas.java
*
* Created on 2006年2月26日, 上午5:54
*/
import javax.microedition.lcdui.*;
/**
*
* @author Allan
*/
public class FullScreenCanvas extends Canvas implements CommandListener
{
public FullScreenCanvas()
{
setTitle("Full screen test");
setTicker(new Ticker("running..."));
addCommand(new Command("full screen", Command.SCREEN, 1));
addCommand(new Command("normal", Command.SCREEN, 1));
setCommandListener(this);
}
public void paint(Graphics g)
{
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
}
public void commandAction(Command c, Displayable s)
{
String cmd = c.getLabel();
if (cmd.equals("full screen"))
{
setFullScreenMode(true);
}
else if (cmd.equals("normal"))
{
setFullScreenMode(false);
}
}
public void sizeChanged(int w, int h)
{
System.out.println("width:" + w);
System.out.println("height:" + h);
}
public void hideNotify()
{
System.out.println("應(yīng)用程序區(qū)域被覆蓋");
}
public void showNotify()
{
System.out.println("屏幕顯示");
}
}
幾個重要觀念:
1) 對于Canvas低級API,標(biāo)題、Ticker、Command區(qū)域依然有用;
2) 全屏模式下,標(biāo)題、Ticker、Command區(qū)域無法顯示,但原本對應(yīng)到按鈕的地方仍然存在,只是看不見;
3) 調(diào)用setFullScreenMode()時,不管設(shè)置成全屏模式還是正常模式,sizeChanged()都會被調(diào)用,并傳入當(dāng)前屏幕的高度和寬度。
4) 當(dāng)屏幕被系統(tǒng)畫面(如菜單、來電顯示等)覆蓋時,會自動調(diào)用hideNotify(),告知應(yīng)用程序目前的畫面被覆蓋了。當(dāng)這些系統(tǒng)畫面消失時,系統(tǒng)將調(diào)用showNotify()告知應(yīng)用程序。Canvas第一次顯示在屏幕上時,系統(tǒng)也會調(diào)用showNotify()。Canvas移出屏幕(有其它displayable被顯示,調(diào)用setCurrent())時,hideNotify()會被調(diào)用。
5) 當(dāng)我們使用Graphics繪圖時,零坐標(biāo)會隨著模式的改變而改變,所以零點不是絕對的,而是相對于應(yīng)用程序區(qū)而言。因為屏幕的大小會改變,所以在清除屏幕(fillRect())的時候使用canvas.getHeight()和canvas.getWidth()取得屏幕大小,而不是固定值。
5.3 鍵盤事件處理
當(dāng)Canvas子類正作用于屏幕時,按下任何按鈕,就會引發(fā)keyPressed()方法,并傳入一個代表該按鈕的整數(shù),而放開按鈕之后,會引發(fā)keyReleased()方法,并傳入一個代表該按鈕的整數(shù)值。系統(tǒng)如果傳入小于0的值,則為不合法keycode。
某些機器上還可以支持連發(fā)事件(即一直按著按鈕持續(xù)一段時間),該事件會引發(fā)keyRepeated()
方法,并傳入一個代表該按鈕的數(shù)值,但該事件并非所有機器都支持,所以我們有必要使用Canvas類中的hasRepeatEvents()方法詢問系統(tǒng)是否支持連發(fā)事件。
示例:
import javax.microedition.lcdui.*;
public class KeyEventTestCanvas extends Canvas
{
private boolean pressed = false;
public KeyEventTestCanvas()
{
}
public void paint(Graphics g)
{
g.setColor(125, 125, 125);
g.fillRect(0, 0, getWidth(), getHeight());
if (pressed)
{
g.setColor(0, 0, 0);
g.drawLine(20, 20, 120, 20);
g.drawLine(20, 20, 20, 100);
g.setColor(255, 255, 255);
g.drawLine(120, 20, 120, 100);
g.drawLine(20, 100, 120, 100);
}
else
{
g.setColor(255, 255, 255);
g.drawLine(20, 20, 120, 20);
g.drawLine(20, 20, 20, 100);
g.setColor(0, 0, 0);
g.drawLine(120, 20, 120, 100);
g.drawLine(20, 100, 120, 100);
}
}
protected void keyPressed(int keycode)
{
pressed = true;
repaint();
}
protected void keyReleased(int keycode)
{
pressed = false;
repaint();
}
}
此例中,把
protected void keyPressed(int keycode)
{
pressed = true;
repaint();
}
protected void keyReleased(int keycode)
{
pressed = false;
repaint();
}
改為
protected void keyPressed(int keycode)
{
repaint();
pressed = true;
}
protected void keyReleased(int keycode)
{
repaint();
pressed = false;
}
結(jié)果是一樣的,這是因為回調(diào)函數(shù)都是在同一個UI的線程中執(zhí)行,因此理論上,repaint()只是產(chǎn)生一個重繪事件就立刻返回,系統(tǒng)必須等到keyPressed()/keyReleased()執(zhí)行完畢之后才能繼續(xù)調(diào)用paint()重繪屏幕。
5.4 鍵盤響應(yīng)
Canvas里定義了幾個常數(shù)
KEY_NUM0、KEY_NUM1 ~ KEYNUM9
KEY_STAR、KEY_POUND共12個
可以利用這幾個常數(shù)判定事件處理方法所傳進來的keyCode,得知那個按鈕被按下。
為了程序可以跨平臺執(zhí)行,建議使用這些標(biāo)準(zhǔn)的定義鍵。
玩游戲的時候通常2、4、6、8分別代表上、下、左、右,星字鍵代表發(fā)射,井字代表跳躍。但并非所有機器上都會有相同的鍵盤排列方式,也并非一定按照此方式設(shè)定。為了設(shè)計Game的時候方便,MIDP規(guī)范中,Canvas中定義了幾個與Game鍵盤代碼相關(guān)的常數(shù),分別是UP、DOWN、LEFT、RIGHT、FIRE、GAME_A、GAME_B、GAME_C、GAME_D。這些定義與之前的定義有所重復(fù),但是因為有了一些抽象性,在移植的時候也就方便多了。
常用方法:
1. getGameAction(int keyCode);
該方法傳入keyCode,會返回所代表的Game鍵盤代碼。
switch(getGameAction(keyCode))
{
case Canvas.FIRE:
fire();
break;
…
}
2. getKeyCode()
該方法傳入Game鍵盤碼,返回所代表的keyCode。
if (keyCode == getKeyCode(Canvas.LEFT))
{
moveLeft();
}
3. 可以利用Canvas的getKeyName()取得該keyCode所代表的按鍵名稱
示例:
import javax.microedition.lcdui.*;
/**
*
* @author Allan
*/
public class KeyEventTestCanvas extends Canvas
{
private boolean pressed = false;
//cross坐標(biāo)
private int x;
private int y;
private final int length = 20;
private int dxy = 5;
/** Creates a new instance of KeyEventTestCanvas */
public KeyEventTestCanvas()
{
x = getWidth()/2;
y = getHeight()/2;
if (this.hasRepeatEvents())
{
System.out.println("支持連發(fā)");
}
else
{
System.out.println("不支持連發(fā)");
}
}
public void paint(Graphics g)
{
g.setColor(128, 128, 128);
g.fillRect(0, 0, getWidth(), getHeight());
paintButton(g, 10, 10, 120, 90, pressed);
paintCross(g, x, y, length);
}
public void paintButton(Graphics g, int x, int y, int w, int h, boolean pressed)
{
if (pressed)
{
g.setColor(0, 0, 0);
g.drawLine(x, y, x+w, y);
g.drawLine(x, y, x, y+h);
g.setColor(255, 255, 255);
g.drawLine(x+w, y, x+w, y+h);
g.drawLine(x, y+h, x+w, y+h);
}
else
{
g.setColor(255, 255, 255);
g.drawLine(x, y, x+w, y);
g.drawLine(x, y, x, y+h);
g.setColor(0, 0, 0);
g.drawLine(x+w, y, x+w, y+h);
g.drawLine(x, y+h, x+w, y+h);
}
}
/*
*@parameter x 十字中心點x坐標(biāo)
*@parameter y 十字中心點y坐標(biāo)
*/
public void paintCross(Graphics g, int x, int y, int length)
{
g.setColor(255, 0, 0);
g.drawLine(x-length, y, x+length, y);
g.drawLine(x, y-length, x, y+length);
}
public void keyPressed(int keycode)
{
//打印keycode代表的按鍵名稱
//System.out.println(getKeyName(keycode));
int action = getGameAction(keycode);
switch (action)
{
case Canvas.UP:
y -= dxy;
break;
case Canvas.DOWN:
y += dxy;
break;
case Canvas.LEFT:
x -= dxy;
break;
case Canvas.RIGHT:
x += dxy;
break;
case Canvas.FIRE:
pressed = true;
break;
}
repaint();
}
public void keyReleased(int keycode)
{
int action = getGameAction(keycode);
switch (action)
{
case Canvas.FIRE:
pressed = false;
break;
}
repaint();
}
public void keyRepeated(int keycode)
{
int action = getGameAction(keycode);
switch (action)
{
case Canvas.UP:
y -= dxy;
break;
case Canvas.DOWN:
y += dxy;
break;
case Canvas.LEFT:
x -= dxy;
break;
case Canvas.RIGHT:
x += dxy;
break;
case Canvas.FIRE:
pressed = true;
break;
}
repaint();
}
}
5.5 觸控屏幕的事件處理
當(dāng)Canvas是當(dāng)前畫面,數(shù)控筆在屏幕上點擊,就會引發(fā)pointerPressed()方法,并傳入其
當(dāng)前處于屏幕的x與y坐標(biāo),放開出控筆后,會引發(fā)pointerReleased()方法,并傳入當(dāng)前屏幕位置的x與y坐標(biāo)。某些機器上可以產(chǎn)生拖拽事件(即一直按住屏幕拖拽),該事件會引發(fā)pointerDragged()方法,并傳入當(dāng)時處于屏幕位置的x與y坐標(biāo)。
并非所有設(shè)備都支持觸控事件,我們可以使用Canva類中的hasPointerEvents()方法獲得系統(tǒng)是否支持觸控筆事件的信息。
并非所有設(shè)備都支持拖拽事件, 我們可以使用Canvas類中的hasPointerMotionEvents()方法判斷系統(tǒng)是否支持觸控筆拖拽事件。
示例:
public class PointerEventTestCanvas extends Canvas implements CommandListener
{
private int x1;
private int y1;
private int x2;
private int y2;
private Command backCommand;
/** Creates a new instance of PointerEventTestCanvas */
public PointerEventTestCanvas()
{
backCommand = new Command("返回", Command.BACK, 1);
addCommand(backCommand);
setCommandListener(this);
if (hasPointerEvents())
{
System.out.println("支持?jǐn)?shù)控筆");
}
else
{
System.out.println("不支持?jǐn)?shù)控筆");
}
if (hasPointerMotionEvents())
{
System.out.println("支持?jǐn)?shù)控筆拖拽");
}
else
{
System.out.println("不支持?jǐn)?shù)控筆事件");
}
}
public void paint(Graphics g)
{
g.setColor(255, 255, 0);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0, 0, 0);
g.drawLine(x1, y1, x2, y2);
}
public void pointerPressed(int x, int y)
{
x1 = x;
y1 = y;
}
public void pointerReleased(int x, int y)
{
x2 = x;
y2 = y;
repaint();
}
public void pointerDragged(int x, int y)
{
x2 = x;
y2 = y;
repaint();
}
public void commandAction(Command c, Displayable s)
{
if (c == backCommand)
{
}
}
}
5.6低級事件和高級事件同時出現(xiàn)
當(dāng)高級事件和低級事件同時出現(xiàn)時,系統(tǒng)會自動判斷。如果按鈕屬于系統(tǒng)的,就會交給高級
事件處理方法來處理,如果不是,才會由低級事件來做。
5.7 繪制字符串
Graphics類提供了繪制字符串的方法,原形如下:
public void drawString(String str, int x, int y, int anchor)
參數(shù):
x、y:相對于屏幕原點的x、y坐標(biāo)
anchor:定位點
注意:即使x、y相同,anchor不同,具體位置還是不同的(后面詳述)
另外,需要了解的是字符串本身顯示要占用屏幕上的一個矩形的空間。anchor的作用就是設(shè)置字符串所處矩形區(qū)域位于屏幕坐標(biāo)的哪個位置。
5.8 Image類
Image 類是我們在處理圖形時常常會用的類別,如果根據(jù)它的產(chǎn)生方式, 我們可以細分成可修改( mutable ) 和不可修改(immutable)兩種。要辨別一個Image 對象是可修改還是不可修改,您可以呼叫Image 對象的isMutable()方法得知。我們可以使用getWidth()與getHeight()取得該Image 對象的寬度與高度。
要產(chǎn)生不可修改的Image 對象,主要方法有三種:
1. 從影像文件讀取:根據(jù)MIDP 的規(guī)定,所實現(xiàn)MIDP 的廠商至少要提供讀取PNG(Portable Network Graphics)影像文件的功能。有些廠商會支持其它如GIF 影像文件的功能,但是不建議使用,因為很可能讓您的MIDlet 無法跨平臺。
2. 由Byte 數(shù)組建立:我們可以經(jīng)由從網(wǎng)絡(luò)或resourcebundle 里的文字文件讀入一連串的byte 數(shù)組,然后用此數(shù)組產(chǎn)生不可修改的Image 對象。
3. 從其它Image 對象(可修改或不可修改皆可)來產(chǎn)生。
范例:
import javax.microedition.lcdui.*;
public class ImageCanvas extends Canvas
{
private Image img;
/** Creates a new instance of ImageCanvas */
public ImageCanvas()
{
try
{
img = Image.createImage("/mario.PNG");
}
catch (Exception e)
{
e.printStackTrace();
}
}
public void paint(Graphics g)
{
g.drawImage(img, 0, 0, g.TOP|g.LEFT);
}
}
在此范例之中,我們使用:img = Image.createImage("/mario.PNG");
從MIDlet Suite 之中讀取名為mario.PNG的圖片。
然后利用g.drawImage(image, 0, 0, g.TOP|g.LEFT);畫出此Image對象。第二種建立不可修改Image 對象的方法是利用其方法:
createImage(byte[] imagedata, int imageOffset, int imageLength)
第三種方法則是利用方法: createImage(Image source)就可以從現(xiàn)有可修改或不可修改的Image 對象取得一份不可修改的拷貝。
可修改的Image 對象
建立一個可修改的Image 對象非常簡單,只要呼叫Image 對象的靜態(tài)方法:
createImage(int width,int height)
即可建立一個可修改的Image 對象。事實上,可修改的Image和Double Buffering 的技術(shù)息息相關(guān),可修改的Image 對象實際上就是一個供人在背景繪圖的off screen。因此在建立可修改的Image 對象前,您應(yīng)該先呼叫Canvas 類別的isDoubleBuffered()函式來確定您的機器是否支持Double Buffering 技術(shù),如果該函式傳回false,那么您就不該試圖建立可修改的Image 對象。
一旦您取得了可修改的Image 對象,我們就可以呼叫Image 類別的getGraphics()取得代表off screen 的Graphics 對象,在off screen 繪圖并不會影響到正常的畫面(on screen)。
最后,我們可以利用on screen ( 由paint 函式傳入的Graphics 物件) 的drawImage()函
式繪出可修改Image 對象的內(nèi)容。
示例:
Image source;
source = Image.createImage(“... ”) ;
//建立可修改Image對象
Image copy = Image.createImage(source.getWidth(), source.getHeight());
Graphics g = copy.getGraphics();//獲取copy的Graphics對象
g.drawImage(source, 0, 0, Graphics.TOP|Graphics.LEFT);
5.9 繪制圖片、文字以及定位點的作用
繪制圖片、字符串或是單一文字都會用到定位點(Anchor)的概念。定位點代表的意義是,繪制圖形跟文字時,所指定的X、Y坐標(biāo)指的是何種意義。
定位點定義共有七種:
Graphics.TOP
Graphics.BOTTOM
Graphics.LEFT
Graphics.RIGHT
Graphics.HCENTER
Graphics.VCENTER
Graphics.BASELINE它們對文字和圖形的控制都具有意義。
注意:圖中標(biāo)有VCENTER參數(shù),主要是因為MIDP1.0提供了該參數(shù)。但MIDP2.0中已經(jīng)不允許使用該參數(shù),主要是因為這個參數(shù)不好計算而且實際使用的意義也不大,如果在MIDP2.0中調(diào)用該參數(shù),將會拋出異常。
這幾種定義可以有意義地組合。舉例來說,如果我們選擇使用TOP 加上LEFT,則繪制文字時,我們會使用函式:
g.drawString(“文字xyzh”, 0, 0, Graphics.TOP|Graphics|LEFT) ;
繪制圖形時,我們會使用函式:
g.drawImage(image, 0, 0, g.TOP|g.LEFT);
這時畫面上的結(jié)果為:
不管是drawString()或是drawImage()其第二與第三個參數(shù)所指定的坐標(biāo)指的是定位點所參考的起始地址。以上述結(jié)果為例,我們指定(0,0)為定位點參考起始位置,然后又選擇的TOP 與LEFT 作為定位點,代表(0,0)這個坐標(biāo)為字符串或圖形繪制在屏幕上時左上角的點。
再舉個例子,如果我們選擇使用BOTTOM 加上HCENTER,則繪制文字時,我們會使用函式:
g.drawString(“文字xyzh”, 0, 0,Graphics.BOTTOM|Graphics.HCENTER) ;
繪制圖形時,我們會使用函式:
g.drawImage(image, 0, 0, g.BOTTOM |g.HCENTER);
這時畫面上的結(jié)果為:
由此我們可以歸納出,如果您使用的方法為:
g.drawString("Hello",x,y,g.TOP|g.LEFT) ;
或
g.drawString("Hello",x,y,0) ;
跟我們使用
g.drawString("Hello",x + stringWidth()/2,y + getHeight(),g.BOTTOM|g.HCENTER) ;
兩者的意義是相同。
思考練習(xí):居中字符串。
5.10 字體
當(dāng)我們需要在屏幕上匯出文字時,我們常常需要一些有關(guān)字體的相關(guān)數(shù)據(jù),這時就需要Font 類別的輔助。通常,我們會使用Font.getDefaultFont()取得代表系統(tǒng)預(yù)設(shè)所使用字型的Font 對象。或者也可以自行使用Font.getFont()來取得代表特定字型的對象。
getFont() 共有三個參數(shù),
Font f = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE) ;
他們分別是外觀( face )、樣式(style)、以及尺寸(size)。他們分別有各種選項:
外觀: Graphics.FACE_MONOSPACE 定寬字體
Graphics.FACE_PROPORTIONAL 比例字體
Graphics.FACE_SYSTEM 系統(tǒng)字體
樣式 : Graphics.STYLE_BOLD 加粗字體
Graphics.STYLE_ITALIC 傾斜字體
raphics.STYLE_PLAIN 常規(guī)字體
Graphics.STYLE_UNDERLINED 下劃線
尺寸 : Graphics.SIZE_LARGE 大號字體
Graphics.SIZE_MEDIUM 中號字體
Graphics.SIZE_SMALL 小號字體
需要注意的是,底層不一定全部支持。getFont()還有另一個只有一個參數(shù)的重載方法,只有PONT_INPUT_TEXT和DON’T_STATIC_TEXT兩種可以選擇,這個方法用來取得系統(tǒng)用來顯示輸入用的字體以及一般常用的字體。
但是最實際程序之中,我們最常使用的是Graphics 類別的getFont()方法取得當(dāng)時顯示在畫面上的屏幕所使用的字型。同理,我們也可以使用Graphics 類別的setFont()方法設(shè)定所使用的字體:
g.setFont(f);
當(dāng)我們?nèi)〉么碜中偷膶ο笾螅覀兙涂梢岳胓etFace()函式取得其外觀、getStyle()取得其樣式、getSize()取得其尺寸。而樣式的部分我們也可以改用isBold()、isItalic()、isPlain()、isUnderlined()函式來取得相關(guān)信息,這是因為樣式是可以合成的,一個字型的樣式很可能同時是黑體與斜體的組合。
Font 類別除了可以幫我們?nèi)〉米中偷耐庥^、樣式與尺寸之外,還能幫助我們?nèi)〉迷撟中驮谄聊簧系南嚓P(guān)屬性,請參考下圖:
Font 的屬性
其中:
charWidth()取得屏幕上某個字符使用該字體顯示時的寬度;
charsWidth()計算一串字符顯示時的寬度;
stringWidth()取得屏幕上某個字符串使用該字體顯示時的寬度;
substringWidth()則是某個字符串的子字符串顯示時的寬度;
getBashLinePosition()可以讓我們知道從字體最頂點到baseline的距離;
getHeight()可以取得最頂點到最低點的距離。
5.11 顏色
Grpahics類提供了3種顏色設(shè)置方法:
public void setColor(int r, int g, int b)
public void setColor(int RGB)
public void setGrayScale(int value)
其中,setGrayScale作用是繪制灰階,只是0到255共256種;setColor方法的作用是設(shè)置RGB顏色模式以在屏幕上顯示彩色的顏色。
RGB模型:對于彩色圖像中的每個RGB(Red、Green、Blue)分量,為每個像素指定一個0(黑色)到255(白色)之間的強度值。當(dāng)三個分量相等時,結(jié)果是中性灰色;當(dāng)所有分量的值均為255時,結(jié)果為白色;當(dāng)這些值都為0時,結(jié)果為純黑色。
setColor(int RGB)的參數(shù)格式是0x00RRGGBB,高2位0x00被忽略,僅僅計算RRGGBB,高2位主要用來計算色彩的Aplpha混合的。
5.12 調(diào)整坐標(biāo)原點
Graphics類提供了調(diào)整屏幕原點位置的方法法:
public void translate(int x, int y)
改變原點的坐標(biāo)在手機屏幕上的位置,并把相對于原來坐標(biāo)系統(tǒng)原點的坐標(biāo)(x,y)作為新的原點。需要注意的是,每次調(diào)用translate方法,都是針對當(dāng)前的坐標(biāo)系統(tǒng)原點進行調(diào)整的,并不是以屏幕左上角進行調(diào)整的。
例如:當(dāng)前坐標(biāo)為(0,0),如果調(diào)用了translate(2,3)則當(dāng)前原點坐標(biāo)為原來屏幕的(2,3)坐標(biāo),如果再調(diào)用translate(4,5),則坐標(biāo)(4,5)是相對于坐標(biāo)(2,3)的,所以相對于最原始的坐標(biāo)系統(tǒng)中的坐標(biāo)(6,8)。
一定要注意每次轉(zhuǎn)移原點都是根據(jù)當(dāng)前屏幕的原點坐標(biāo)為標(biāo)準(zhǔn)的。
Grphics還提供了2個計算目前坐標(biāo)與原始狀態(tài)下原點坐標(biāo)的距離的方法。它們是: getTranslateX()和getTranslateY()。
因為一個坐標(biāo)系統(tǒng)可以進行多次坐標(biāo)原點的轉(zhuǎn)移,如果希望原點恢復(fù)到原始狀態(tài),只要再轉(zhuǎn)移一個負變量就是原點往坐標(biāo)軸的負方向移動,可以使用如下代碼恢復(fù)原始狀態(tài)的圓點位置:
g.translate(-getTranslateX(), -getTranslateY()) ;
如果希望在已經(jīng)轉(zhuǎn)移了原點的環(huán)境中使用絕對位置(ax,ay),絕對位置就是指相對于原始狀態(tài)的原點位置:
g.translate(ax – getTranslateX(), ay – getTranslateY());
5.14 裁剪區(qū)
在處理圖像時,經(jīng)常會碰到圖片比較大,而屏幕只能顯示圖像一部分的情況。因此需要確定圖像中哪些部分位于顯示區(qū)域內(nèi),而哪些內(nèi)容落在顯示區(qū)域之外,以便只顯示落在顯示區(qū)域內(nèi)的那部分圖像。這個選擇的過程成為裁剪。
裁剪區(qū)的作用:只有在裁剪區(qū)域內(nèi)的繪圖過程才會真正有效,在區(qū)域外的無效,即使在區(qū)域外之行了繪圖方法也是不會顯示的。
Graphics類提供了簡單的裁剪方法,原形如下:
public void setClip(int x, int y, int width, int height)
setClip可以設(shè)置需要裁剪的開始坐標(biāo)(x,y),然后指定矩形的裁剪區(qū)域的寬和高。當(dāng)屏幕重繪的時候,可以保證屏幕的其它部分的內(nèi)容不必重繪,僅僅重繪需要更新的部分內(nèi)容。一般使用了裁剪方法以后,應(yīng)該恢復(fù)到原來的裁減區(qū)域。
例:
int oldClipX = g.getClipX();
int oldClipY = g.getClipY();
int oldClipWidth = g.getClipWidth();
int oldClipHeight = g.getClipHeight();
//設(shè)置裁減區(qū)域
g.setClip( 50, 50, 100, 100);
//恢復(fù)原來的裁減區(qū)域
g.setClip(oldClipX, oldClipY, oldClipWidth, oldClipHeight);
5.15 重繪機制
當(dāng)需要重繪屏幕的時候,可以調(diào)用Canvas類的repaint()方法,然后程序會自動調(diào)用paint()方法完成重繪,如果僅僅需要屏幕的一部分更新,可以使用repaint方法的一個重載方法,指定需要更新區(qū)域的坐標(biāo)以及寬度和高度,請求部分屏幕重繪, 例如:
repaint(10, 10, 50, 50);
重繪起始坐標(biāo)為(10, 10),寬度為50, 高度為50的屏幕區(qū)域。
5.16 雙緩存技術(shù)
雙緩存技術(shù)是計算機動畫的一項傳統(tǒng)技術(shù)。
屏幕閃爍的主要原因在于:一幅畫面正在顯示的時候,程序在改變它,然后在一幅圖片還沒有完全顯示完畢又請求重新繪制,于是表現(xiàn)為畫面閃爍。
解決閃爍的辦法:在內(nèi)存中操作需要處理的圖片,程序?qū)?nèi)存中的圖片更新、修改、完成后再顯示它。這樣顯示出來的圖片永遠是完全畫好的圖像,程序修改的將不是正在被顯示
的圖像。
5.17 動畫制作
基本的動畫,是將顯示在屏幕上的角色等的動作與描繪的位置作連續(xù)的變化,產(chǎn)生動態(tài)的效果。
<轉(zhuǎn)載:http://blog.csdn.net/allan_sun/archive/2006/08/09/1043157.aspx >