張亞利 吳彥國
摘要:在進行Java GUI編程時,程序利用paint(Graphics g)方法來實現(xiàn)組件的繪制;然而Java的繪圖機制是怎樣的,paint方法又是何時調(diào)用、如何調(diào)用,且每次調(diào)用時paint方法的執(zhí)行次數(shù)是怎樣的?輕量級和重量級組件調(diào)用paint有哪些區(qū)別等,本文作者將針對這些問題,用案例分析的方法來一一解答。
關(guān)鍵詞:繪圖機制;paint;調(diào)用
學(xué)過Java程序設(shè)計語言的朋友都知道,在學(xué)習Java GUI編程時,我們都知道paint方法是用來繪制圖形的。有了paint方法,我們可以在Java中繪制各種漂亮的組件,或開發(fā)漂亮炫酷的游戲。但是paint是如何調(diào)用的?程序啟動時,paint方法執(zhí)行了一次還是多次,原理是什么?另外Swing組件和AWT組件對super.paint方法是如何相應(yīng)的,組件在處理動畫時,對KeyListner又是如何響應(yīng)的呢?關(guān)于這些細節(jié)性的問題,許多初學(xué)者總是模棱兩可。筆者經(jīng)過查找資料,并寫程序進行驗證,得出一些結(jié)果,以供分享。
問題1:Java的繪圖機制是怎樣的?
AWT是使用回調(diào)機制來處理繪畫的,并且這種機制對輕量級和重量級組件相同。也就是說我們需要把渲染組件的代碼寫在一個可覆蓋的特殊的方法中,這個方法是public void paint(Graphics g);其中 Graphics是圖形上下文對象,用來完成具體的繪制工作。paint方法由AWT框架調(diào)用或者由程序自身通過repaint方法來調(diào)用。
問題2:畫圖時,paint是何時調(diào)用,如何調(diào)用,調(diào)用幾次的問題。
Java繪圖時,用到的paint方法繼承自AWT中的Component方法,該方法在對象加載時自動調(diào)用,用來繪制該組件內(nèi)部的所有圖形圖像。使用repaint()方法,可以再次調(diào)用paint方法,實現(xiàn)組件的重繪。但是paint方法是什么時候開始執(zhí)行,執(zhí)行了幾次呢?下面我們用例子,來加以分析。
public class Test extends Applet//引入包的代碼省略
{Image img = null;int t =0;
public void init(){System.out.print("初始化====");
img = getImage(getCodeBase(),"1.jpg");}
public void paint(Graphics g)
{t++;System.out.print("我是paint===="+t+"");
g.fillRect(50,50,80,80);//代碼段1,繪制矩形
// g.drawImage(img,50,50,this); //代碼段2,繪制圖形}}
執(zhí)行代碼段1的結(jié)果:
初始化====我是paint====1
執(zhí)行代碼段2的結(jié)果:
初始化====我是paint====1我是paint====2……我是paint====47
從上面結(jié)果可以發(fā)現(xiàn),啟動瀏覽器時,系統(tǒng)自動調(diào)用init()方法進行程序初始化,接著系統(tǒng)自動找到paint()方法對圖像進行繪制。而且paint()方法調(diào)用的次數(shù),跟繪制的內(nèi)容有關(guān),圖形簡單時,可能執(zhí)行一次,圖像復(fù)雜時,可能會自動調(diào)用很多次,直到圖形圖像完全繪制成功。另外還有當組件大小發(fā)生改變,或組件有壞點(damage)需要被修復(fù)時,系統(tǒng)都會自發(fā)地調(diào)用repaint方法,進行組件的重繪。
問題3:Java程序繪圖時,JPanel和Panel對super.paint()的響應(yīng)問題。
Java通過調(diào)用paint()方法來繪制組件,但是 AWT框架中輕量級組件與重量級組件實現(xiàn)代碼還是有區(qū)別的。
輕量級組件的繪制依賴與包含關(guān)系階層中的重量級祖先組件,當這個祖先組件被通知繪制時,它將把繪制請求轉(zhuǎn)化為繪制自身上任何可見的子孫組件,這個方法是由java.awt.Container中的 paint() 方法來完成的,因此任何Container的子類,在覆蓋paint方法時一定要記得調(diào)用super.paint()來保證,它上面的輕量級子孫組件都被繪制到了。代碼可以這樣來完成:
public class MyContainer extends Container {
public void paint(Graphics g) { // 先繪制自身內(nèi)容, 然后確保輕量級子組件被繪制
super.paint(g);}}
下面我們將通過案例,來觀察:
class myPanel extends JPanel implements KeyListener //重要代碼:繼承JPanel/Panel
{int x=50;public myPanel(){addKeyListener(this);} //為面板增加鍵盤監(jiān)聽器
public void paint(Graphics g){super.paint(g);//重要代碼段:super.paint
g.setColor(Color.orange); g.fillRect(x, 50, 60, 60);}//設(shè)置畫筆顏色,填充矩形
public void keyPressed(KeyEvent e){x=x+10;repaint();//釋放鍵盤,矩形向右移動10像素;}
public void keyTyped(KeyEvent e){}
public void keyReleased(KeyEvent e){}}
創(chuàng)建myPanel對象p,設(shè)置背景色為青色,同時在面板p上添加標簽“測試super.paint()”,程序?qū)⒊霈F(xiàn)四種情況,對應(yīng)3種效果。
(1)面板繼承JPanel,且paint方法中調(diào)用super.paint(),所得結(jié)果如圖2。
(2)面板繼承JPanel,且paint方法中沒有調(diào)用super.paint(),所得結(jié)果如圖1。
(3)面板繼承Panel,且paint方法中沒有調(diào)用super.paint(),所得結(jié)果如圖3。
(4)面板繼承Panel,且paint方法中調(diào)用super.paint(),所得結(jié)果如圖2。
問題4:處理動畫時,JPanel和JApplet等Swing組件對KeyListner無響應(yīng)的問題。
為面板增加鍵盤監(jiān)聽器KeyListner,若面板繼承java.awt.Panel,那么當程序運行時,每敲擊一次鍵盤,上述案例中的矩形方塊開始向右移動10個像素的距離;但是若面板繼承javax.swing.JPanel,敲擊鍵盤,則無任何相應(yīng)動作。這又是什么原因引起的呢?
經(jīng)過實驗分析,筆者發(fā)現(xiàn)JPanel等Swing組件在渲染完成后,無法獲取focus,所以我們只需要上層容器setvisible(true)之前,先設(shè)置 p.requestFocus();為面板對象獲取焦點即可。
以上分析,希望對愛好Java GUI編程的學(xué)習者提供幫助。