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