熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

雙緩沖原理在awt和swing中實現消除閃爍的方法

2013-11-23 19:31:52  來源: Java核心技術 
    對於雙緩沖的分析是在坦克大戰游戲的設計時開始的由於當時忙於游戲的整體設計所以對這一個問題沒有進行詳細的研究現在就這個問題來談談自己的一些看法
分析前提出幾個問題
    為什麼當想屏幕上添加圖片之後會有明顯的閃爍現象?
    在awt中如何實現雙緩沖?
    如何理解swing內置雙緩沖以及比較他與awt中消除閃爍的方法區別在哪裡?
    首先我們來解答第一個問題
    我們在屏幕上自繪圖形或者是添加圖片都是要通過所在畫布的重繪來實現的因此閃爍的出現必然與重繪機制有著一些關聯在awt中對於窗體畫布的重繪其條用順序是repaint() —>update()—>paint();我們來看看update()的源碼
Java代碼
    /**
    * Updates the container  This forwards the update to any lightweight
    * components that are children of this container  If this method is
    * reimplemented superupdate(g) should be called so that lightweight
    * components are properly rendered  If a child component is entirely
    * clipped by the current clipping setting in g update() will not be
    * forwarded to that child
    *
    * @param g the specified Graphics window
    * @see
    Component#update(Graphics)
    */
    public void update(Graphics g) {
    if (isShowing()) {
    if (! (peer instanceof LightweightPeer)) {
    gclearRect( width height);
    }
    paint(g);
    }
    }
    從這裡我們可以清晰的看到update中有一個清屏的作用即gclearRect( width height);然後再在下面調用paint(g)函數進行重繪因此到這裡的話我們可以在一定程度上對底層的重繪機制有一個了解了


    現在我們明白了屏幕上之所以出現閃爍是因為在update()方法內先要嘩嘩的清空屏幕上原有的東西然後又嘩嘩的往上畫所以在我們需要不斷重繪的屏幕上出現閃爍是必然的了哪怕CPU的速度快之又快
    通過上述的分析在awt中我們解決閃爍問題的思路也因該隨之產生即重寫update()函數的代碼改變它的工作原理於是我們引進一段在坦克大戰中已經重寫了的update()方法其中通過改變重繪函數paint(g)重繪的畫布對象由窗體的畫布變為截取的圖片上的畫布gImage這樣的話就很大程度上改善這個問題了具體如下
Java代碼
    // 重寫update方法先將窗體上的圖形畫在圖片對象上再一次性顯示
    public void update(Graphics g) {
    if (offScreenImage == null) {
    // 截取窗體所在位置的圖片
    offScreenImage = thiscreateImage(WIDTH HEIGHT);
    }
    // 獲得截取圖片的畫布
    Graphics gImage = offScreenImagegetGraphics();
    // 獲取畫布的底色並且使用這種顏色填充畫布(默認的顏色為黑色)
    Color c = ColorBLACK;
    gImagesetColor(c);
    gImagefillRect( WIDTH HEIGHT); // 有清除上一步圖像的功能相當於gImageclearRect( WIDTH HEIGHT)
    // 將截下的圖片上的畫布傳給重繪函數重繪函數只需要在截圖的畫布上繪制即可不必在從底層繪制
    paint(gImage);
    //將接下來的圖片加載到窗體畫布上去才能考到每次畫的效果
    gdrawImage(offScreenImage null);
    }
    其實一言以蔽之就是通過重寫update()方法改變重繪函數paint(g)重繪的畫布對象g
    以上的討論我們都是在awt中進行然後大家就想將繼承Frame改為JFrame試試結果一試就傻眼了屏幕上居然又是嘩嘩的閃了真是辛辛苦苦去改變一下回到解放前我們不是在update()中實現雙緩沖機制了嗎?請看下面的一個對比測試
    ()在awt中測試update():
    Java代碼
    // 重寫update方法先將窗體上的圖形畫在圖片對象上再一次性顯示
    public void update(Graphics g) {
    Systemoutprintln(awt的update()在此);
    if (offScreenImage == null) {
    // 截取窗體所在位置的圖片
    看看結果
    要是沒覺得意外的話就繼續往下看
    在swing中測試update():
    Java代碼
    // 重寫update方法先將窗體上的圖形畫在圖片對象上再一次性顯示
    public void update(Graphics g) {
    Systemoutprintln(Swing的update()在此);
    if (offScreenImage == null) {
    // 截取窗體所在位置的圖片
結果是
    是不是有點吃驚了在我沒有故意編出這個東西忽悠大伙的前提下我們可以得知在swing中update()方法並沒有像awt的update()那樣隨時被調用所以就很好解釋為什麼該為繼承JFrame之後屏幕重繪閃爍了就是你認為自己改寫了update()方法就會解決這個問題是一廂情願的系統並不買你的帳調都沒去調用吶!


    那麼怎麼通過其他的方法消除swing中的閃爍問題呢我們此時再回到出發點雙緩沖的核心就是改變paint(g)中的畫布那麼好了我直接在paint(g)函數裡實現不就得了下面再來看這一段代碼
 Java代碼
    public void paint(Graphics g)
    {
    // 在重繪函數中實現雙緩沖機制
    offScreenImage = thiscreateImage(WIDTH HEIGHT);
    // 獲得截取圖片的畫布
    gImage = offScreenImagegetGraphics();
    // 獲取畫布的底色並且使用這種顏色填充畫布如果沒有填充效果的畫則會出現拖動的效果
    gImagesetColor(gImagegetColor());
    gImagefillRect( WIDTH HEIGHT); // 有清楚上一步圖像的功能相當於gImageclearRect( WIDTH HEIGHT)
    // 調用父類的重繪方法傳入的是截取圖片上的畫布防止再從最底層來重繪
    superpaint(gImage);
    // 當游戲沒有結束的時候繪出對戰雙方
    if (!getGameOver()) {
    // 畫出自己的坦克
    paintMyTank(gImage);
    // 畫出自己坦克發射的子彈
    paintMyBullet(gImage);
    // 畫出敵方坦克
    paintEnemyTank(gImage);
    // 畫出敵方坦克發射的子彈
    paintEnemyBullet(gImage);
    }
    // 畫出草地
    paintGrass(gImage);
    // 畫出小河
    paintRiver(gImage);
    // 畫出石頭
    paintStone(gImage);
    // 畫出各種道具
    paintTool(gImage);
    // 將接下來的圖片加載到窗體畫布上去才能考到每次畫的效果
    gdrawImage(offScreenImage null);
    }
    有一些相似的部分吧其中最重要的是superpaint(gImage)這句改變畫布在這裡消除閃爍也是在這裡!!!
    下面我們再探討最後一個問題即如何理解swing中內置雙緩沖我們首先從繼承體系來看JFrame>Frame>Window>Container>Component在Frame中的update()方法是從Container中繼承而來的而JFrame中卻重寫了update()方法如下
Java代碼
    /**
    * Just calls paint(g)  This method was overridden to
    * prevent an unnecessary call to clear the background
    *
    * @param g the Graphics context in which to paint
    */
    public void update(Graphics g) {
    paint(g);
    }
    與之前的同名方法相比這裡直接調用了paint()函數而沒有clearRect()也就是清屏的方法這裡他試圖不通過清屏來阻止閃爍的發生這也就是JFrame本身的一種處理方法
    以上是通過自己對雙緩沖的一些理解其中還有很多問題希望牛人們能夠積極指出來並且一起討論這個問題


From:http://tw.wingwit.com/Article/program/Java/hx/201311/27043.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.