Как устранить мерцание анимации Java - PullRequest
7 голосов
/ 25 марта 2012

Я пишу 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 ]

7 голосов
/ 25 марта 2012
  1. В JPanel переопределите paintComponent(Graphics) вместо paint(Graphics)
  2. Вместо вызова Thread.sleep(n) реализуйте Swing Timer для повторяющихся задач или SwingWorker для продолжительной работызадачи.См. Параллельность в Swing для получения более подробной информации.
6 голосов
/ 25 марта 2012

JPanel по умолчанию имеет двойную буферизацию, и "программы Swing должны переопределять paintComponent() вместо переопределения paint()." - Рисование в AWT и Swing: Методы рисования .Сравните это пример , который переопределяет paintComponent() с этим примером , который переопределяет paint().

Добавление: сдвиг вызывается созданием и копированием всего изображения на каждомОбновить.Вместо этого накапливайте баллы на GeneralPath и draw() на Shape на каждой итерации, как показано здесь .

...