Click here to Skip to main content
15,885,651 members
Articles / Programming Languages / Java / Java SE
Article

Double buffer in standard Java AWT

Rate me:
Please Sign up or sign in to vote.
4.75/5 (10 votes)
16 Apr 20023 min read 137K   438   30   5
Create a double buffered base-class using standard Java AWT.

Introduction

Have you ever created an application that showed an animation in your window? You probably have, and if you have not, you probably will someday. The coding is really simple: you take a Panel and you add a thread to it that calls the repaint method every x milliseconds. Done?

No, it's not quiet over yet. Because, if you leave it like that, the screen will flash and flicker, and your animation will end up partially painted, or not painted at all.

The solution for this is the infamous double buffer.

AWT versus SWING

In the Swing framework, you can add the double buffer just by setting this property to true in your class. Swing is carrying out all of the tasks needed for you. They're behind the scenes, and all that you have to know is that it's done. The flicker is gone. Thank you mister Sun.

But what if you use the standard AWT? The Swing framework is rather heavy and still has some nasty bugs, so, on older machines, it's more likely that you use the standard AWT. But, there's no double buffer here, isn't it?

I've studied this problem for some while now, and this is the solution I've come up with. It is a solution, not the solution. I've seen others too, but they all have more or less the same components.

The DoubleBuffer Class

Since we are good Java programmers, we create a base-class for this, so we can implement the double buffer functionality whenever we feel like. You can choose between the classes Component, Container, Canvas and Panel to extend, but I suggest you choose the Panel class. If you choose another, you can get problems later on, with some event listeners that will not fire properly.

JavaScript
import java.awt.*;

public class DoubleBuffer extends Panel{

    public DoubleBuffer(){
        super();
    }
}

Eliminating the Flicker

What's causing the flicker? Well, if you call the repaint method, you actually tell the VM to repaint this component as soon as possible. You can't tell when (but you can set an initial delay). When the VM has time to carry out the paint tasks, it calls the update method for you. And there's the first problem already. The update method clears the panel for you, so you can paint to it, without concerning about the background. That's what's causing the flicker. It clears the panel, shows in the window, paints the panel and shows again. So every paintjob has in fact two jobs, clearing and painting.

We're going to deprecate this.

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

public void paint(Graphics g){
}

Now, the background isn't cleared anymore, so the flicker is gone. If you use this to animate through some images with the same dimensions, you are finished here already, because you don't have to clear the previous image.

But what if you have to clear the previous paintjob?

The Buffer Image

The idea behind double buffering is that you first paint everything to an off-screen image, and, when it's ready, paint it to the screen in just one paintjob. For this purpose, we need a bufferimage and it's brother buffergraphics. This image has always the same dimensions as your panel, so if your panel resizes, the image has to resize also, and the previous has to be cleared out of memory.

JavaScript
//    class variables
private int bufferWidth;
private int bufferHeight;
private Image bufferImage;
private Graphics bufferGraphics;

public void paint(Graphics g){
    //    checks the buffersize with the current panelsize
    //    or initialises the image with the first paint
    if(bufferWidth!=getSize().width ||
      bufferHeight!=getSize().height ||
      bufferImage==null || bufferGraphics==null)
        resetBuffer();
}

private void resetBuffer(){
    // always keep track of the image size
    bufferWidth=getSize().width;
    bufferHeight=getSize().height;

    //    clean up the previous image
    if(bufferGraphics!=null){
        bufferGraphics.dispose();
        bufferGraphics=null;
    }
    if(bufferImage!=null){
        bufferImage.flush();
        bufferImage=null;
    }
    System.gc();

    //    create the new image with the size of the panel
    bufferImage=createImage(bufferWidth,bufferHeight);
    bufferGraphics=bufferImage.getGraphics();
}

So, your bufferimage has been created and now it's ready to picasso. We add some functionality to the paint method and create a paintbuffer method. After that the off-screen image has to be copied to the screen. Everything is painted on the screen in one go, so bye bye flicker.

JavaScript
public void paint(Graphics g){
    ...    resetBuffer();

    if(bufferGraphics!=null){
        //this clears the offscreen image, not the onscreen one
        bufferGraphics.clearRect(0,0,bufferWidth,bufferHeight);

        //calls the paintbuffer method with
        //the offscreen graphics as a param
        paintBuffer(bufferGraphics);

        //we finaly paint the offscreen image onto the onscreen image
        g.drawImage(bufferImage,0,0,this);
    }
}

public void paintBuffer(Graphics g){
    //in classes extended from this one, add something to paint here!
    //always remember, g is the offscreen graphics
}

Now, your double buffer class is ready!

Extending the new class

Now, everything you have to do is to extend DoubleBuffer and deprecate the paintBuffer method instead of the paint method. In your application, you can call the repaint method whenever you feel like, and the screen is not going to flicker once.

public class MyBuffer extends DoubleBuffer{

    private int posX;

    public MyBuffer(){
        super();

        posX=0;
    }

    public void animateToTheRight(){
        //    this can be called from everywhere, anytime
        posX++;
        repaint();
    }

    //    attention: we don't touch update() and paint() anymore
    //    we use paintbuffer() instead

    public void paintBuffer(Graphics g){
        /// g is the offscreen graphics
        g.drawString("Double Buffer!",posX,20);
    }
}

In the example, if you add a thread and let it call animateToTheRight(), a string is scrolling from left to right, without causing any flicker.

Voila, the double buffer is working.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Belgium Belgium
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey26-Feb-12 19:24
professionalManoj Kumar Choubey26-Feb-12 19:24 
GeneralMy vote of 4 Pin
jafar4all188-Aug-10 22:36
jafar4all188-Aug-10 22:36 
QuestionFlicker between AWT window and dialog [modified] Pin
wrappingduke1-Oct-07 8:31
wrappingduke1-Oct-07 8:31 
Hello,

I've created a class that shows a tooltip window for awt components. However, the problem that I'm having is, that there appears to be a flicker between the tooltip window and a dialog component.

The Tooltip class has a java.awt.Label to show the text of the tooltip. I'm wondering if this maybe the cause of the flicker? When the label is repainted, it has to be cleared?

Here's a sample of Tooltip code:

// constructor
private ToolTipManager() {
super(); // added to DoubleBuffer class **jf 10/01/07
label.setBackground(BACKGROUND_COLOR);
timerThread.start();
}

private void createWindow()
{
// Find the Frame parent of currentComponent.
Component top = currentComponent;
while (true)
{
Container parent = top.getParent();
if (parent == null) break;
top = parent;
}

// Use that Frame for the parent of the tooltip Window.
window = new Window((Frame) top);
window.add(label, BorderLayout.CENTER);
}

public synchronized void run() {
while (true) {
try {
// Wait until notified that the cursor is over a Component
// that has a tooltip.
synchronized (this) {
wait();
}

Thread.sleep(WAIT_TIME);

// Get the tooltip text to be displayed.
String tip =
(String) componentToTipTable.get(currentComponent);

// set font size and alignment of label
Font oFont = new Font(Font.SANS_SERIF, Font.PLAIN, 10);

repaint(); // override method to eliminate flicker **jf 10/01/07

label.setFont(oFont);
label.setAlignment(label.CENTER);
label.setText(tip);

if (window == null) {
createWindow();
}

window.pack();

// Place the tooltip directly below its Component.
Rectangle bounds = currentComponent.getBounds();
Point location = currentComponent.getLocationOnScreen();
window.setLocation(location.x, location.y + bounds.height);

window.setVisible(true);

} catch (InterruptedException e) {
// Don't break out of loop! Just wait for the mouse
// to move over another component that has a tooltip.
}
}

any help is appreciated.

-- modified at 14:36 Monday 1st October, 2007
QuestionThe size of the Panel is always 0 Pin
neuquino11-Dec-05 12:35
neuquino11-Dec-05 12:35 
AnswerRe: The size of the Panel is always 0 Pin
Zizilamoroso12-Dec-05 4:07
Zizilamoroso12-Dec-05 4:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.