How to remove flickering java animations

I am writing a Java application that is supposed to scroll the waveform smoothly across the screen. I spent a lot of time on various tutorials to figure out how to make this animation as smooth as possible. As far as I can tell, I did all the normal things to eliminate flickering (drawing on the screen buffer and rendering in one step, plus overriding the update so that the screen is not blank), but my animation still flickers, and the screen looks like it was muffled before each update.

I'm sure there is something fundamental (and probably simple) that I miss, but I have no ideas. I will post below a class that illustrates the problem. Any help would be greatly appreciated.

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); } } 
+7
source share
2 answers
  • In JPanel override paintComponent(Graphics) , not paint(Graphics)
  • Instead of calling Thread.sleep(n) implement Swing Timer to repeat tasks or SwingWorker for lengthy tasks. See Concurrency in Swing for more details.
+7
source

JPanel is double-buffered by default, and "Swing programs should override paintComponent() instead of overriding paint() ." - Painting in AWT and Swing: Paint methods . Compare this example , which overrides paintComponent() with this example , which overrides paint() .

Addendum: The shift is caused by the creation and copying of the entire image with each update. Instead, accumulate points on the GeneralPath and draw() Shape on each iteration, as shown here.

+6
source

All Articles