2012-03-25 5 views
7

私は、画面上をスムーズにスクロールする必要のあるJavaアプリケーションを作成しています。私はこのアニメーションをできるだけスムーズにする方法を理解するために、さまざまなチュートリアルを経て年月をかけました。私が見る限り、ちらつきをなくすためにすべての通常の作業を行っています(オフスクリーンのバッファに描画し、1回でレンダリングし、更新を上書きして画面が空白にならないようにします)。しかし、アニメーションはまだちらつきます。各更新の前に空白になっているようです。Javaアニメーションのちらつきを解消する方法

私には基本的な(そしておそらくシンプルな)ものがあると確信していますが、私はアイデアがありません。問題を説明するクラスを投稿します。どんな助けでも大歓迎です。

import java.awt.*; 
import javax.swing.*; 

public class FlickerPanel extends JPanel implements Runnable { 

    private float [] pixelMap = new float[0]; 

    /** Cached graphics objects so we can control our animation to reduce flicker **/ 
    private Image screenBuffer; 
    private Graphics bufferGraphics; 

    public FlickerPanel() { 
     Thread t = new Thread(this); 
     t.start(); 
    } 

    private float addNoise() { 
     return (float)((Math.random()*2)-1); 
    } 

    private synchronized void advance() { 
     if (pixelMap == null || pixelMap.length == 0) return; 
     float [] newPixelMap = new float[pixelMap.length]; 
     for (int i=1;i<pixelMap.length;i++) { 
      newPixelMap[i-1] = pixelMap[i]; 
     } 

     newPixelMap[newPixelMap.length-1] = addNoise();  

     pixelMap = newPixelMap; 
    } 

    public void run() { 
     while (true) { 
      advance(); 
      repaint(); 

      try { 
       Thread.sleep(25); 
      } catch (InterruptedException e) {} 

     } 
    } 

    private int getY (float height) { 
     double proportion = (1-height)/2; 
     return (int)(getHeight()*proportion); 
    } 

    public void paint (Graphics g) { 

     if (screenBuffer == null || screenBuffer.getWidth(this) != getWidth() || screenBuffer.getHeight(this) != getHeight()) { 
      screenBuffer = createImage(getWidth(), getHeight()); 
      bufferGraphics = screenBuffer.getGraphics(); 
     } 

     if (pixelMap == null || getWidth() != pixelMap.length) { 
      pixelMap = new float[getWidth()]; 
     } 

     bufferGraphics.setColor(Color.BLACK); 

     bufferGraphics.fillRect(0, 0, getWidth(), getHeight()); 

     bufferGraphics.setColor(Color.GREEN); 

     int lastX = 0; 
     int lastY = getHeight()/2; 

     for (int x=0;x<pixelMap.length;x++) { 
      int y = getY(pixelMap[x]); 
      bufferGraphics.drawLine(lastX, lastY, x, y); 
      lastX = x; 
      lastY = y; 
     } 

     g.drawImage(screenBuffer, 0, 0, this); 
    } 

    public void update (Graphics g) { 
     paint(g); 
    } 

    public static void main (String [] args) { 
     JFrame frame = new JFrame("Flicker test"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setContentPane(new FlickerPanel()); 
     frame.setSize(500,300); 
     frame.setVisible(true); 
    } 


} 
+0

コードで問題が見つかりません。デスクトップ上でかなり滑らかに見えます。 ps。 1つのpixelMapを持ってそれを巡回バッファーとして使用し、同期化されたブロックでアクセスを保護するだけで、これをもっと効率的に行うことができます。新しい構造を作成する際には、それぞれの更新がOKである必要があります。 – Adam

+0

+1 [sscce](http://sscce.org/)。それは私のプラットフォーム上でかなりひどく震える。 – trashgod

+0

もう少しプレイしたことで、paintMacのデータ構造がペイント中に変更されていることが原因の可能性があります。そのため、画面のさまざまな部分の間で裂けてしまいます。ペイントメソッドの開始時にpixelmapのclone()を行うと改善されるようですが、ちらつきはまだ残っていますので、これがすべての答えではないと思います。 –

答えて

6

JPanelデフォルトでダブルバッファされ、そして "Swingのプログラムではなくpaint()をオーバーライドするpaintComponent()をオーバーライドする必要があります。" - Painting in AWT and Swing: The Paint Methods。コントラストがexampleで、オーバーライドがpaintComponent()の場合、examplepaint()を上書きします。

補足:各更新で画像全体を作成してコピーすることによって、せん断が発生します。代わりに、hereのように、GeneralPathdraw()の各反復で点を累積します(Shape)。 JPanel

+0

私はpaintComponentをオーバーライドしようとしましたが、ダブルバッファリングされないようにパネルを設定しましたが、どちらも違いはないようですが、ちらつきはまだ発生します。 –

+0

この関連[例](http://stackoverflow.com/a/5048863/230513)は点滅しません。編集:それは 'javax.swing.Timer'に依存してEDTを更新します。 – trashgod

+0

私は上記で詳しく述べました。ノイズについては、['nextGaussian()'](http://docs.oracle.com/javase/7/docs/api/java/util/Random.html#nextGaussian%28%29)も考慮してください。 – trashgod

7
  1. paintComponent(Graphics)はなくpaint(Graphics)
  2. 代わりの長時間実行されるタスクのためのタスクまたはSwingWorkerを繰り返すスイングTimerを実装 Thread.sleep(n)を呼び出すをオーバーライドします。 詳細はConcurrency in Swingを参照してください。
+1

はい。 'sleep()'が別のスレッドで発生し、 'repaint()'がスレッドセーフであるとしても、 'advance()'は同期させる必要があります。 – trashgod

+0

OK、それは良い点ですが、私はそれがちらつきに直接影響を与えるとは思わない - それは私が上書きする方法と同じように見える。 –

+1

@trashgod私はポイント2の最初の文を削除することにしました* "EDT(イベントディスパッチスレッド)をブロックしないでください - GUIがその時にフリーズします" *ここでは当てはまりません。 –