騎豬闖天下

          J2ME隨筆,記錄成長的腳步

          統(tǒng)計

          留言簿(3)

          閱讀排行榜

          評論排行榜

          [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 >

          posted on 2008-09-01 19:43 騎豬闖天下 閱讀(1298) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 崇义县| 团风县| 千阳县| 山丹县| 苏尼特左旗| 比如县| 四子王旗| 长宁县| 平邑县| 永吉县| 沂南县| 墨脱县| 宁德市| 大化| 临澧县| 苍南县| 炉霍县| 新宾| 蕉岭县| 乌什县| 湖口县| 鄢陵县| 调兵山市| 贵溪市| 团风县| 即墨市| 信宜市| 新蔡县| 通山县| 通州市| 姜堰市| 涡阳县| 南皮县| 神木县| 平江县| 阿巴嘎旗| 西和县| 阿瓦提县| 南投市| 余姚市| 淳安县|