如何消除java动画闪烁

时间:2012-03-25 09:43:30

标签: java swing animation concurrency flicker

我正在编写一个需要在屏幕上平滑滚动波形的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);
    }


}

2 个答案:

答案 0 :(得分:7)

  1. JPanel中,覆盖paintComponent(Graphics)而不是paint(Graphics)
  2. 而不是打电话 Thread.sleep(n)为重复任务实现Swing Timer或为长时间运行的任务实现SwingWorker。 有关详细信息,请参阅Concurrency in Swing

答案 1 :(得分:6)

JPanel默认为双缓冲,&#34; Swing程序应覆盖paintComponent()而不是覆盖paint()。&#34; - Painting in AWT and Swing: The Paint Methods。将覆盖paintComponent()的{​​{3}}与覆盖paint()的此example进行对比。

附录:剪切是由在每次更新时创建和复制整个图像引起的。相反,在每次迭代时积累点GeneralPathdraw() Shape,如example所示。