用慣的J2ME進階UI而還沒有接觸過低級UI的developer一定不會習慣跟Canvas的初夜!因為他沒有Form那麼多可以觸摸到風情萬種,也沒有Item的獨特風味,有的只是一個只畫筆一個畫布,一些只有名字的事件回應程式法,更奇怪的事情是你不能手動的調用paint()去做你想做的事情,一切你都只能任憑系統擺布!今日話題 — J2ME Canvas 開發小解 [請注意不是小姐,你可以理解為詳解] !
所要將的幾個問題:
一.首先是一些朋友的疑惑:
1.如何去實現一個J2ME Canvas的輸入框?想TextField那種!
從這裡開始基本就進入我們的正題了。在現在這個人工智慧技術還相當不成熟的電腦時代,使用者互動最重要的凡是無非就是向電腦系統提供資料和指令讓其返回執行結果以供我們使用。所以,但你需要開發一個J2ME程式的時候,資料互動是必不可少的,當受夠了LCDUI那醜陋的介面(或者說不個性的介面,實際上就是系統調用嘛)的時候,Canvas和CustumItem是一個最容易想到的辦法,實際上要從底層實現的話也只有這個方法。由於CusomItem還是擺脫不了Command的束縛,所以Canvas是你最自由的空間,但是自用永遠都是屬於強者的。不要奢望很簡單的一個new就能完成。NOW BEGIN …
要想在Canvas裡面直接實現一個類似於TextField那樣的輸入框是不可能的!請注意:它的意思是“畫布”,也就是說之能畫東西。不要著急,會給你筆的 — Graphics ! 要如何去實現一個輸入框呢? 這樣的: 畫一個矩形 — 響應使用者點擊事件 — 開啟TextBox — 完成輸入 — 返回輸入結果 — 將得到的結果畫在剛才畫的那個矩形裡面 ! 搞定
也看到有的說,在底層自己去實現一個IME。我覺得這是一個極其愚蠢的說法,我用ARM處理再加上一個液晶顯示器完成一個輸入的功能,其調用過程是相當繁瑣的。呵呵,所以想要自己實現IME並顯示字型的暫時打住吧。
2.如何在Canvas裡面實現一個捲軸?用起來UE(User Experance)一定能好一點!
要完成一個捲軸,需要做兩件事: 1.判斷是否需要捲軸;2.去繪製這個捲軸 。 Canvas不會在你需要ScrollBar的時候自己出現,她永遠沒有isNeeded()這種好事提供給你。下面將會有一些樣本性代碼。
3.如何去實現一個用J2ME Canvas做到菜單?這是必須的。
所謂菜單,也就是螢幕上面顯示的像菜單的東西罷了,說到底還是要自己畫的,只是選擇畫的位置不一樣而已。好了,稍候給出實現方式。
4.Canvas中IME的實現?!好難的問題。
上面已經說了,可能性不太大,要不自己去看J2ME的原始碼,可以試試。(去參考當開啟TextField的時候都使用了些什麼系統調用,哈哈)
5.Canvas之間為啥會出現重疊現象?如何去做到讓一個Canvas隱藏起來?
出現Canvas映像重疊這個問題其實是很簡單的,因為你都是畫在螢幕上面的啊,而螢幕只有一個,再因為Canvas是透明的,呵呵。這個是預設值。所以,一般情況下如果你不需要其他顏色的畫就是在初始化的時候將Canvas全部填充為白色,如下:
[方法一]int i = 0 ;
while(i < this.getHeight()){
g.drawLine(0,i,this.getWidth(),i,Graphics.Top|Graphics.LEFT);
}
[方法二]
g.fillRect(0,0,this.getWidth(),this.getHeight());
6.如何退出一個Canvas?或者說怎麼從一個Canvas切換到另外一個Canvas?
Canvas類沒有提供類似於hidden()這樣的方法,而是只提供了一些檢查型的方法,例如 isShow() 。而還有例如showNotify()這樣的方法都是系統去調用的。這個通常是當你調用setCurrent()的時候系統回去將需要顯示的現實處理。所以,要做Canvas之間實現切換無非就是這麼做: setCurrent(ANOTHER Displayable) 。
二.小總結一下,闡釋一下疑惑:
造成諸多的不可操作和操作繁瑣的原因無非就是一個:JAVA希望你自由。你要自由的話,那就自己去畫吧,想怎麼畫怎麼畫,想怎麼做怎麼做。一切都要你自己去實現。
【FIRST】J2ME Canvas提供的一些我們要用到的方法:
在J2ME遊戲編程中,Canvas類是最常用的類之一,該類提供了獲得手機螢幕屬性、繪製介面以及事件處理等很多實用的功能,下面就系統的介紹一下該類的使用,並結合實際說明一下在實際的使用過程中需要注意的一些問題。
Canvas類是Displayable的子類,主要用來需要處理低級事件,例如鍵盤按鍵事件等,以及需要繪製螢幕的程式。在實際的使用過程,一般都通過繼承Canvas來利用該類提供的功能。Canvas類是一個抽象類別,繼承該類的時候必須覆蓋paint方法。
Canvas類的功能主要包含以下幾類:
1、 獲得手機螢幕屬性
getHeight——獲得螢幕可用高度
getWidth——獲得螢幕寬度
isDoubleBuffered——是否支援雙緩衝
hasPointerEvents——是否支援指標裝置
hasPointerMotionEvents——是否支援指標動作,例如拖拉事件
hasRepeatEvents——是否支援重複按鍵
實際開發過程中,可以在程式中直接調用這些方法,通過傳回值獲得相應的屬性資訊。
2、 繪製方法
paint——繪製方法
repaint——重新繪製方法
paint方法需要在介面類中覆蓋,然後就可以書寫功能,而再需要重新繪製的時候手動調用repaint方法。
3、 事件處理
低級使用者介面的事件處理分為兩種:按鍵事件和指標事件(處理觸控螢幕手機的低級事件)。
支援按鍵事件的方法主要有三個:
keyPressed——按鍵按下的事件
keyReleased——按鍵釋放的事件
keyRepeated——重複按鍵的事件
支援指標事件的方法也有三個:
pointerPressed——指標裝置按下事件
pointerReleased——指標裝置釋放事件
pointerDragged——指標裝置拖拉事件
在實際的編程中,只需要在介面類中覆蓋這些方法,然後在方法的內部書寫代碼即可,在發生這些事件的時候,系統會自動調用這些方法。
4、 其他方法
getGameAction——將按鍵映射成遊戲動作
getKeyCode——將遊戲動作轉換為索引值
getKeyName——將索引值轉換為按鍵名稱
hideNotify——在Canvas介面被隱藏的時候系統自動調用該方法
showNotify——當Canvas介面顯示的時候系統自動調用該方法
serviceRepaints——強制系統重新繪製
以上方法除了hideNotify、showNotify需要在子類中覆蓋,系統會自動調用以外,其他的方法都可以直接調用。
5、 系統的熟悉主要分為兩個部分:
按鍵的索引值
KEY_NUM0、KEY_NUM1、KEY_NUM2、KEY_NUM3、KEY_NUM4、KEY_NUM5、KEY_NUM6、KEY_NUM7、KEY_NUM8、KEY_NUM9分別對應手機鍵盤的0-9數字鍵,KEY_STAR對應*號鍵,KEY_POUND對應#號鍵。各個功能鍵的索引值在Canvas類沒有進行定義,所以各個廠商,甚至廠商的不同型號手機之間,的索引值都有所不同。但是功能鍵的索引值均小於0。
遊戲動作
UP、DOWN、LEFT、RIGHT和FIRE,分別對應上、下、左、右和確定鍵,在實際的手機中一般分別對應2、8、4、6和5鍵以及功能鍵中的方向鍵。GAME_A、GAME_B、GAME_C、GAME_D分別對應遊戲中的A、B、C和D鍵,分別映射成手機上的1、3、7、9鍵,或者是7、9、*和#鍵。
在實際的事件處理中,使用遊戲可以達到在不同的手機之間通用。
在MIDP2.0中,又新增了兩個方法,分別是:
setFullScreenMode——控制螢幕是全螢幕顯示還是一般模式顯示
sizeChanged——當螢幕尺寸變化的時候,系統會自動調用該方法。
上面系統的介紹了Canvas類提供的功能,下面就介紹一下它的使用。當調用Display對象的setCurrent方法顯示Canvas介面時,系統首先調用Canvas對象的構造方法,然後調用paint方法實現繪製,這樣我們就可以看到實際的介面了。而以後,當介面座標發生變化以後,必須手動調用repaint方法實現繪製。
以下是一些在實際的編程過程中,需要注意的一些問題:
座標參數化或者根據螢幕的寬度和高度產生座標,從而提高介面的移植性。
在程式運行過程中,paint方法經常被反覆調用,所以一般在該方法中只放置繪製的代碼,而把邏輯處理的代碼放在別的位置。
在進行低級事件處理代碼編寫中,對於遊戲按鍵,盡量使用遊戲動作進行編程,其他按鍵才直接使用索引值,從而提高事件處理代碼的移植性。
因為hideNotify和showNotify方法會在介面隱藏和重新顯示時,被系統自動調用,所以可以覆蓋這兩個方法,在其中實現暫停邏輯。
盡量使用雙緩衝技術,避免螢幕的閃爍,除非手機不支援雙緩衝。
顯示其他介面的時候,注意要關閉線程,並釋放資源。
在Canvas裡面永遠沒有Form裡面那些append啊,addxxxx啊之類的方法,因為那樣你就不自由了,所以JAVA不會去做一些束縛你的事情,感謝上蒼吧!
三.J2ME Canvas 開發小試牛刀:
1.實現Loading效果(進度載入):
要實現Loading效果,你必須要做的一件事情就是使用多線程!這樣用起來比較爽。
….
//先畫一個小矩形,圓角不圓角你說了算
g.fillRect(10,10,80,20); // 畫了一個寬80,高20的矩形,做進度條很不錯哦
new Thread(new Runnable(){
public vodi run () {
g.fillRect(10+delta,10,20,20); //畫一個X座標值為動態小矩形在上面那個大矩形中,你的loading效果出來怎麼樣就完全看你的動態X和小矩形的寬度如何設定了
}
}).start();
….
2.實現一個Canvas表單(輸入框,按鈕,映像):
這裡就可以涉及到事件響應了,Canvas給我們提供了keyPressed(int key)來響應使用者的按鍵 。
protected void paint(){
g.drawRect(20,20,80,30);
}
protected void keyPressed(int key){
int action = getGameAction(key);
switch(action){
case Canvas.FIRE : EventHandle.doInput(); break ;
}
repaint();
}
上面這段代碼展示了一個如何去實現一個輸入事件的響應,也就是當使用者按下通常的OK鍵的時候會去做輸入這件事情,至於怎麼做就看你的了,不過一般情況就是去開啟一個TextBox完成輸入之後再把使用者的輸入繪製到矩形裡面。
3.錨點:
在畫布中錨點實際上就是座標原點,取值有類似於這些:
【 Graphics.Top|Graphics.LEFT 】
【 Graphics.Top|Graphics.RIGHT 】
【 Graphics.BOTTOM|Graphics.LEFT 】
【 Graphics.BOTTOM|Graphics.RIGHT 】
【 Graphics.VCENTER|Graphics.HCENTER 】
【 Graphics.HCENTER|Graphics.TOP 】
【 Graphics.HCENTER|Graphics.BOTTOM 】
【 Graphics.VCENTER|Graphics.LEFT 】
【 Graphics.HCENTER|Graphics.RIGHT 】
根據意思你就會明白是哪裡了,這些值必須成對使用,因為是用的邏輯運算子 “|” 所以,每一對值的次序也就不重要了。
4.如何去處理不同手機平台索引值不一樣的問題:
由於手機種類實在太多,你不可能完全瞭解,或者每一個程式都為每一種機型開發一個版本。那樣是不現實的,也是效率低劣的。所以,我的做法是,讓使用者來確定。哈哈,方法就是首次啟動並執行時候讓使用者告訴我們每個索引值是多少,然後記錄下來就OK啦,哈哈,還不用移植。
5.[引用]關於J2ME中雙緩衝技術:
雙緩衝技術是編寫J2ME遊戲程式的關鍵技術之一。實際上,雙緩衝技術是電腦動畫的一項傳統技術。造成螢幕閃爍的主要原因在於,畫面在顯示的同時程式又在改變它,於是畫面閃爍。
解決辦法是在記憶體中開闢一片地區作為後台畫面,程式對它更新、修改,完成後再顯示它。這樣被顯示的映像永遠是已經完全畫好的映像,程式修改的將不是正在被顯示的映像。當然還有其他方法可以解決螢幕閃爍問題,但使用雙緩衝技術是一種值得推薦的解決方案。
有些裝置本身就支援雙緩衝,每次都是先把螢幕重畫在緩衝之中,然後再繪製在顯示螢幕上,而不是直接繪製在顯示螢幕上。可以使用Canvas類的isDoubleBuffer方法判斷裝置是否具有雙緩衝。
可變映像可以很容易地用作螢幕外緩衝。改寫前面繪製不變映像的代碼,將所有的繪製都放在可變映像中,然後一次性地將可變映像繪製到螢幕上去。
package doublebufferdemo;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import java.io.*;
public class ImageCanvas extends Canvas
{
private Image buffer; //可變映像,作為繪製緩衝
private Image image; //不變映像,用來載入圖片檔案
public ImageCanvas()
{
try
{
image = Image.createImage("/tree.png"); //載入圖片檔案
}catch(java.io.IOException e)
{
System.out.println(e.getMessage()); //處理I/O異常
}
buffer = Image.createImage(this.getWidth(), this.getHeight());
//用一個可變映像作為繪製緩衝
Graphics bg = buffer.getGraphics(); //擷取緩衝的Graphics對象
bg.setColor(0xFFFFFF);
bg.fillRect(0, 0, getWidth(), getHeight()); //填充整個螢幕
bg.drawImage(image,this.getWidth()/2,this.getHeight()/2, <a href="http://www.sunjianyes.cn/Article/tag/graphics">Graphics</a>.VCENTER|Graphics.HCENTER);
}
public void paint(Graphics g)
{
g.drawImage(buffer,0,0,g.TOP|g.LEFT); //將緩衝區上的內容繪製到螢幕上
}
}
編譯、運行程式,其結果和前面完全相同,但是卻採用了雙緩衝技術。對於雙緩衝的使用,可以總結出以下幾點。
* 定義一個Graphics對象bg和一個Image對象buffer,按螢幕大小建立一個緩衝對象賦給buffer,然後取得buffer的 Graphics對象賦給bg。在這裡,Graphics對象可以理解為緩衝的螢幕,Image對象則可當成緩衝螢幕上的圖片。
* 在bg(緩衝螢幕)上用drawImage()和drawString()等語句畫圖,相當於在緩衝螢幕上畫圖。
* 調用repaint()語句,它的功能是告知系統調用paint()來完成真實螢幕的顯示。這裡需要注意的是,paint()是一個系統調用語句,不能手工調用,只能通過paint()語句來調用。
* 在paint(Graphics g)函數裡,將buffer(緩衝螢幕上的圖片)畫到真實螢幕上。
以上的步驟雖然看似繁瑣,但是本身在不支援雙緩衝機制的時候是必須的,回過頭來看效果還是很不錯的。如果想在螢幕上顯示什麼東西,只要畫在bg上,然後調用repaint()將其顯示出來就可以了。
6.比較好的調用paint(Graphics g)的方式:
paint(Graphics g) {
init(g);
yourPainter_1(g);
yourPainter_(g);
}
init(Graphics g){
// ......
}
yourPainter_1(Graphics g){
// ......
}
yourPainter_2(Graphics g){
// ......
}
上面所示這種是我認為比較好的一種使用paint方法的辦法,推薦下!
7.關於J2ME Graphics的畫筆
g.setStrokeStyle(Graphics.DOTTED);
g.setStrokeStyle(Graphics.SOLID);
Graphics只有這兩種畫筆,一個視線一個點線,需要什麼就是什麼。