2

I have a simple applet that animates a rectangle along the x-axis of the canvas. The problem is that it flickers. I have tried to Google this problem, but I didn't come up with anything useful or anything that I understood.

I am relatively new to Java.

Thanks!

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*; 

public class simpleAnimation extends JApplet implements ActionListener { 
    Timer tm = new Timer(10, this); 
    int x = 0, velX = 2;

    public void actionPerformed(ActionEvent event) { 
        if (x < 0 || x > 550){ 
            velX = -velX; 
        }

        x = x + velX; 
        repaint(); 
    }

    public void paint ( Graphics g ) { 
    super.paint(g); 
    g.setColor(Color.RED); 
    g.fillRect(x, 30, 50, 30); 
    tm.start(); 
    } 
}

**********UPDATED CODE WITHOUT FLICKER**********

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

public class simpleAnimation extends JApplet implements ActionListener  
{ 

     Graphics bufferGraphics; 

     Image offscreen; 

     Dimension dim; 

     int x = 3, velX = 2;

     Timer tm = new Timer(10, this);

     public void init()  
     { 

          dim = getSize(); 

          offscreen = createImage(dim.width,dim.height); 

          bufferGraphics = offscreen.getGraphics(); 
     }

      public void paint(Graphics g)  
     { 

          bufferGraphics.clearRect(0,0,dim.width,dim.width); 

          bufferGraphics.setColor(Color.red); 

          bufferGraphics.fillRect(x,50,50,20); 

          g.drawImage(offscreen,0,0,this); 

          tm.start();   

     }

     public void update(Graphics g) 
     { 

          paint(g); 

     } 

    public void actionPerformed(ActionEvent evt) 
    {   

        if ( x < 0 || x > 550){

            velX = -velX;

        }

        x = x + velX;   

        repaint();

    }

 }

I used this applet as a template.

this_guy
  • 594
  • 1
  • 12
  • 24
  • 4
    You need to [double buffer](http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html). – Obicere Nov 04 '13 at 18:40
  • possible duplicate of [Java Applet flickering when running? (not everybody sees flickering)](http://stackoverflow.com/questions/18584009/java-applet-flickering-when-running-not-everybody-sees-flickering) – Obicere Nov 04 '13 at 18:44
  • 1
    The `paint()` is culprit of the flickering. –  Nov 04 '13 at 18:50
  • Swing is double buffered by default, you don't need to do anything special. – camickr Nov 04 '13 at 19:18

3 Answers3

3

I always struggle with this concept of double buffering.

Here is my example that overrides paint() of the JApplet and a paintComponent() of a JPanel, which uses double buffering by default.

I don't see any difference in the apparent flickering.

//<applet code="SimpleAnimation.class" width="600" height="300"></applet>
import java.awt.*;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;

public class SimpleAnimation extends JApplet implements ActionListener {
    Timer tm = new Timer(10, this);
    int x = 0, velX = 2;
    JPanel panel;

    public void init()
    {
        panel = new JPanel()
        {
            @Override
            public Dimension getPreferredSize()
            {
                return new Dimension(50, 100);
            }

            @Override
            protected void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                g.setColor(Color.RED);
                g.fillRect(x, 30, 50, 30);

            }
        };
        add(panel, BorderLayout.SOUTH);

        tm.start();
    }

    public void actionPerformed(ActionEvent event) {
        if (x < 0 || x > 550){
            velX = -velX;
        }

        x = x + velX;
        repaint();
//        panel.repaint();
    }

    public void paint ( Graphics g ) {
    super.paint(g);
    g.setColor(Color.RED);
    g.fillRect(x, 30, 50, 30);
    }
}

I test the code using: appletviewer SimpleAnimation.java

Is my concept of double buffering flawed, or my implementation, or both?

camickr
  • 321,443
  • 19
  • 166
  • 288
  • The bottom rectangle's animation is a lot smoother than what is shown on the video. – this_guy Nov 04 '13 at 20:22
  • Yes, that is what I am asking. @this_guy, If the bottom animation is smoother then you should be using this approach for you custom painting. This is the way painting is done is Swing (as was already mentioned by MadProgrammer). Your approach of overriding paint() and update() is done with AWT components, not Swing components. Also this approach is simpler since you don't need to manage a buffered image. – camickr Nov 04 '13 at 20:23
  • I see the flicker on you youtube video, but on my machine both the top and bottom animations are the same and then are better than the video. I guess the video card, CPU have an affect on the animation. – camickr Nov 04 '13 at 20:26
  • Thanks for the help. Do you know of any good (beginner) java tutorial sites other than http://docs.oracle.com/javase/tutorial ? A lot of sites I find using Google seem to be outdated. – this_guy Nov 04 '13 at 20:29
  • I always start with that tutorial. – camickr Nov 04 '13 at 20:34
1

What you're doing now works like this:

public void paint ( Graphics g ) {
  // draw the entire area white
  super.paint(g);
  g.setColor(Color.RED);
  // draw a rectangle at the new position
  g.fillRect(x, 30, 50, 30);
}

So with every step, you first wipe out your rectangle, and then draw it fresh. Thus the flickering - the pixels under the rectangle keep changing from white to red to white to red to white to red...

Now observe that the smallest amount of painting you need to do is (supposing rectangle moves to the right) this:

  • draw velx pixels on the left WHITE
  • draw velx pixes on the right RED

If you do that, your animation will be smooth.

Computing that can be quite challenging though, especially when your shape is more complicated than just a square / your movement is more complex. That's where double buffering comes in.

With double buffering, you create an in-memory image that is the same size as your screen. You paint your entire theme there. Then you paint that image on your screen all at once.

When doing that, there won't be an intermediate step of "entire screen is WHITE"; thus no flickering. But note that you end up re-painting the entire screen rather than just the area that changed, which isn't ideal. Consider using clipping - a technique where you repaint a pre-defined area of the image and not the entire thing.

iluxa
  • 6,941
  • 18
  • 36
  • not s/he would need to put there JPanel (all painting to the Top-Level Containers (by default is) caused with flickering) and to override paintComponent and to use JFrame instead of JApplet – mKorbel Nov 04 '13 at 19:10
1

The problem is, top level containers like JApplet aren't double buffered. This means that when it's updated, the screen flickers as each action is done directly onto the screen.

Instead, you should create a custom component, using something like a JPanel, and override its paintComponent method and perform your custom painting actions there.

Because Swing components are double buffered the results of the paint action are buffered before they are painted to the screen, making it a single action

Take a look at Performing Custom Painting for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Hmm, I see this apparently works for the OP. I don't see a difference. Any comments on my code? @this_guy, I would like to see the code you implemented to see if there is any difference on my computer. – camickr Nov 04 '13 at 20:04
  • @camickr The significant difference between what you've done and what the OP has done is that the OP can no longer take advantage of the Swing API (or at least to it's fullest) what I remember...and it takes more work. The OP's approach is a direct rendering approaching, meaning they control when the repaint occurs, unlike Swing which is a passive rendering API, updating as it needs... – MadProgrammer Nov 04 '13 at 20:09
  • Sorry, when I made my comment, I didn't see the updated code and I thought the OP was using your suggestion to do the custom painting on a JPanel (since that was your suggestion). I don't see a difference when I run my example. They both animate better the the video the OP provided on my answer. I guess the video card and CPU affect the animation? – camickr Nov 04 '13 at 20:34
  • The video card will defiantly have an effect as will the underlying pipeline – MadProgrammer Nov 04 '13 at 20:37
  • Got to love blind downvotes, which to comment as to why the answer is wrong or unhelpful – MadProgrammer Oct 09 '16 at 00:23
  • There seems to be a contradiction: you say JApplet isn't double buffered, but then you say Swing components *are* double buffered. But JApplet is a Swing component. Did you mean to say something like "most Swing components" or "Swing components that aren't top-level containers"? – Don Hatch Aug 10 '20 at 08:42
  • I guess it depends on you definitions - more accurately, "top level swing containers", like, `JFrame`, `JWindow`, `JDialog` and `JApplet` aren't double buffered :P – MadProgrammer Aug 10 '20 at 09:03