Search code examples
javaimageswingtimerimageicon

How do I remove the flicker between changing the image that is displayed in a label?


I have four images that I am cycling between to create a simple animation. Each image is a frame of a repeating animation and I have a timer change the image to the next frame to make it animated. Every time I change the image there is a flicker where the window is all white in between displaying the next image. How do I remove this flicker?

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

public class Reveal extends JFrame
{
    private JPanel panel = new JPanel(); //a panel to house the label
    private JLabel label = new JLabel(); //a label to house the image
    private String[] image = {"Jack in the Box 1.png","Jack in the Box 2.png","Jack in the Box 3.png","Jack in the Box 4.png","Jack in the Box 5.png","Jack in the Box 6.png","Jack in the Box 7.png"}; //an array to hold the frames of the animation
    private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images

    private Timer timer;
    private Timer timer2;
    int x = 0;
    int y = 4;
    int counter = 0;
/**
 * Constructor for objects of class Reveal
 */
public Reveal()
{
    for (int h = 0; h < 7; h++){
      icon[h] = new ImageIcon(image[h]);
      icon[h].getImage().flush();
      label.setIcon(icon[h]);
    }

    //Display a title.
    setTitle("Raffel");

    //Specify an action for the close button.
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Sets the size of the window
    setSize(800,850);
    panel = new JPanel();
    label = new JLabel();
    panel.add(label);

    add(panel);

    setVisible(true);
}

public void display(String name, int number){
    timer = new Timer(150, new ActionListener(){
        public void actionPerformed(ActionEvent e) {
            if (counter > 48){
            timer.stop();
            timer2.start(); //starts the second half of the animation
          }else{
            label.setIcon( icon[x] );
            if (x != 3){
                x++;
            }else{
                x = 0;
            }
            counter++;
          } //ends if-else
        } //ends action method
    }); //ends timer

    timer2 = new Timer(150, new ActionListener(){
        public void actionPerformed(ActionEvent e) {
          if (y > 6) {
            timer2.stop();
          }else{
            label.setIcon( icon[y] );
            y++;
          } //ends if-else
        } //ends action method
    }); //ends timer2

    timer.start();
    }
}

Solution

  • icon[x] = new ImageIcon(image[x]);
    icon[x].getImage().flush();
    label.setIcon( icon[x] );
    

    I would suggest that you don't keep reading the images from disk.

    Load all the ImageIcons into an array at the start of your class and then just cycle through the array to get the next Icon to update the label.

    Edit:

    The animation has only 4 unique frames.

    But you have an array of 7 icons.

    icon[h].getImage().flush();
    

    There is no need for the flush. You are just creating ImageIcons.

    label.setIcon(icon[h]);
    

    Why do you keep setting the icon of the label each time you create a new Icon? This means the last Icon created will be the first Icon displayed.

    I would expect you should assign icon[0] to the label AFTER the loop is finished. I would guess this is the cause of the flicker because the last icon is briefly displayed before the first. So if you default to the first you won't have a problem.

    The animation has only 4 unique frames. The animations is 64 frames long playing each image as a frame 16 times

    You don't need 2 Timers for this. You need two variables.

    1. one that keeps incrementing until 64 and then stops the animation
    2. one that keeps incrementing to 3 and then resets to 0