2012-03-25 2 views
7

Я пишу приложение Java, которое должно прокручивать форму волны плавно по экрану. Я потратил много времени на различные обучающие программы, чтобы выяснить, как сделать эту анимацию максимально гладкой. Насколько я могу судить, я делал все нормальные вещи, чтобы устранить мерцание (рисование на экранный буфер и рендеринг за один шаг, плюс переопределение обновления, чтобы экран не был пустым), но моя анимация все еще мерцает, а экран похоже, что он закрывается перед каждым обновлением.Как удалить 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

Я не могу найти никаких проблем с кодом. Выглядит довольно гладко на моем рабочем столе. пс. Вы можете сделать это намного эффективнее, имея только одну пиксельную карту и используя ее в виде циклического буфера и защищая доступ к ней с помощью синхронизированного блока. По мере создания новой структуры каждое обновление должно быть в порядке. – Adam

+0

+1 для [sscce] (http://sscce.org/). Это очень плохо мерцает на моей платформе. – trashgod

+0

Проиграв немного больше, кажется, что некоторые из проблем могут быть связаны с изменением структуры данных pixelMap во время рисования, поэтому я разрываю между различными частями экрана. Выполнение клона() пикселя в начале метода рисования, кажется, улучшает ситуацию, но мерцание все еще существует, поэтому я не думаю, что это весь ответ. –

ответ

6

JPanel является двойной буферизации по умолчанию, и "программы Качели должны переопределить paintComponent() вместо переопределения paint()." - Painting in AWT and Swing: The Paint Methods. Контрастируйте этот example, который переопределяет paintComponent() с этим example, который переопределяет paint().

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

+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. В JPanel, переопределить paintComponent(Graphics) вместо paint(Graphics)
  2. Вместо вызова Thread.sleep(n) реализовать качание Timer для повторяющихся задач или SwingWorker для длительных задач. Для получения более подробной информации см. Concurrency in Swing.
+1

Да; даже если 'sleep()' встречается в другом потоке и 'repaint()' является потокобезопасным, 'advance()' необходимо синхронизировать. – trashgod

+0

Хорошо, это хороший момент, но я не думаю, что это имеет прямое влияние на мерцание - он кажется тем же самым, какой метод я переопределяю. –

+1

@trashgod Я решил удалить первое предложение пункта 2 * «Не блокируйте EDT (Thread Dispatch Thread) - графический интерфейс« замораживается », когда это произойдет». * Поскольку в то время как true, это, похоже, не применяется здесь , –

Смежные вопросы