歡迎您光臨本站 註冊首頁

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

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0
對於雙緩衝的分析是在坦克大戰遊戲的設計時開始的,由於當時忙於遊戲的整體設計,對這一個問題沒有進行詳細的研究,現在就這個問題來談談自己的一些看法.
分析前提出幾個問題:
1、為什麼當想屏幕上添加圖片之後會有明顯的閃爍現象?
2、在awt中如何實現雙緩衝?
3、如何理解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, super.update(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)) {
g.clearRect(0, 0, width, height);
}
paint(g);
}
}
從這裡我們可以清晰的看到,update中有一個清屏的作用,即g.clearRect(0, 0, width, height);然後再在下面調用paint(g),函數進行重繪.因此到這裡的話我們可以在一定程度上對底層的重繪機制有一個了解了.
現在我們明白了,屏幕上之出現閃爍是在update()方法內先要嘩嘩的清空屏幕上原有的東西,然後又嘩嘩的往上畫,在我們需要不斷重繪的屏幕上出現閃爍是必然的了,哪怕CPU的速度快之又快.
通過上述的分析,在awt中我們解決閃爍問題的思路也因該隨之產生,即重寫update()函數的代碼,改變它的工作原理.於是我們引進一段在坦克大戰中已經重寫了的update()方法.其中通過改變重繪函數paint(g)重繪的畫布對象,由窗體的畫布變為截取的圖片上的畫布gImage,這樣的話就很大程度上改善這個問題了.具體如下
Java代碼
// 重寫update方法,先將窗體上的圖形畫在圖片對象上,再一次性顯示
public void update(Graphics g) {
if (offScreenImage == null) {
// 截取窗體所在位置的圖片
offScreenImage = this.createImage(WIDTH, HEIGHT);
}
// 獲得截取圖片的畫布
Graphics gImage = offScreenImage.getGraphics();
// 獲取畫布的底色並且使用這種顏色填充畫布(默認的顏色為黑色)


Color c = Color.BLACK;
gImage.setColor(c);
gImage.fillRect(0, 0, WIDTH, HEIGHT); // 有清除上一步圖像的功能,相當於gImage.clearRect(0, 0, WIDTH, HEIGHT)
// 將截下的圖片上的畫布傳給重繪函數,重繪函數只需要在截圖的畫布上繪製即可,不必在從底層繪製
paint(gImage);
//將接下來的圖片載入到窗體畫布上去,才能考到每次畫的效果
g.drawImage(offScreenImage, 0, 0, null);
}
其實一言以蔽之就是通過重寫update()方法改變重繪函數paint(g)重繪的畫布對象g.
以上的討論我們都是在awt中進行,然後大家就想將繼承Frame改為JFrame試試,結果一試就傻眼了,屏幕上居然又是嘩嘩的閃了,真是辛辛苦苦去改變,一下回到解放前,我們不是在update()中實現雙緩衝機制了嗎?請看下面的一個對比測試:
(1)在awt中測試update():
Java代碼
// 重寫update方法,先將窗體上的圖形畫在圖片對象上,再一次性顯示
public void update(Graphics g) {
System.out.println("awt的update()在此...");
if (offScreenImage == null) {
// 截取窗體所在位置的圖片
看看結果:
要是沒覺得意外的話就繼續往下看
在swing中測試update():
Java代碼
// 重寫update方法,先將窗體上的圖形畫在圖片對象上,再一次性顯示
public void update(Graphics g) {
System.out.println("Swing的update()在此...");
if (offScreenImage == null) {
// 截取窗體所在位置的圖片
結果是:
是不是有點吃驚了,在我沒有故意編出這個東西忽悠大夥的前提下我們可以得知,在swing中update()方法並沒有像awt的update()那樣隨時被調用,就很好解釋為什麼該為繼承JFrame之後屏幕重繪閃爍了.就是你認為自己改寫了update()方法就會解決這個問題是一廂情願的,系統並不買你的帳,調都沒去調用吶!
那麼怎麼通過其他的方法消除swing中的閃爍問題呢,我們此時再回到出發點,雙緩衝的核心就是改變paint(g)中的畫布,那麼好了,我直接在paint(g)函數里實現不就得了,下面再來看這一段代碼:
Java代碼
public void paint(Graphics g)
{
// 在重繪函數中實現雙緩衝機制
offScreenImage = this.createImage(WIDTH, HEIGHT);
// 獲得截取圖片的畫布
gImage = offScreenImage.getGraphics();
// 獲取畫布的底色並且使用這種顏色填充畫布,如果沒有填充效果的畫,則會出現拖動的效果
gImage.setColor(gImage.getColor());
gImage.fillRect(0, 0, WIDTH, HEIGHT); // 有清楚上一步圖像的功能,相當於gImage.clearRect(0, 0, WIDTH, HEIGHT)
// 調用父類的重繪方法,傳入的是截取圖片上的畫布,防止再從最底層來重繪


super.paint(gImage);
// 當遊戲沒有結束的時候繪出對戰雙方
if (!getGameOver()) {
// 畫出自己的坦克
paintMyTank(gImage);
// 畫出自己坦克發射的子彈
paintMyBullet(gImage);
// 畫出敵方坦克
paintEnemyTank(gImage);
// 畫出敵方坦克發射的子彈
paintEnemyBullet(gImage);
}
// 畫出草地
paintGrass(gImage);
// 畫出小河
paintRiver(gImage);
// 畫出石頭
paintStone(gImage);
// 畫出各種道具
paintTool(gImage);
// 將接下來的圖片載入到窗體畫布上去,才能考到每次畫的效果
g.drawImage(offScreenImage, 0, 0, null);
}
有一些相似的部分吧,其中最重要的是super.paint(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本身的一種處理方法.
以上是通過自己對雙緩衝的一些理解,其中還有很多問題,希望牛人們能夠積極指出來,並且一起討論這個問題.


[火星人 ] 雙緩衝原理在awt和swing中實現消除閃爍的方法已經有1318次圍觀

http://coctec.com/docs/java/show-post-60013.html