Proper way to use JLabels to update an image

I am creating a GUI, and am fairly new to swing and awt. I am trying to create a gui that, upon launch, sets the background to an image, then uses a method to create a slideshow of sorts. I have attempted it, and I am not attached to the code so I am able to take both revisions and/or whole new concepts.

EDIT(9/15/13): I am having trouble with the slideshow, I cant seem to get it to work.

Here is my current code.

public class MainFrame extends JFrame{

JLabel backgroundL = null;
private JLabel bakckgroundL;
BufferedImage backimg;
Boolean busy;
double width;
double height;

public MainFrame() throws IOException {
    initMainframe();
}



public void initMainframe() throws IOException { 

//misc setup code, loads a default jpg as background

    setTitle("Pemin's Aura");
    busy = true;
    String backgroundDir = "resources/frame/background.jpg";

    backimg = ImageIO.read(new File(backgroundDir));
    backgroundL = new JLabel(new ImageIcon(backimg));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    refreshframe();
    setVisible(true);
    busy = false;
}
public void adjSize() { // the attempted start of a fullscreen mode
        GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
    width = this.getWidth();
    height = this.getHeight();
    setVisible(true);
}

public void setmastheadText() {//unfinished code
busy = true;

busy = false;
}
public void setbackground() {
    add(backgroundL);
}
public void refreshframe() { //should refresh image?
    setSize(2049, 2049);
    setSize(2048, 2048);
}
public void loadingscreen() throws IOException, InterruptedException {

 //this is the code in question that is faulty.

    if (busy == false) {
    busy = true;

    String backgroundDir1 = "resources/frame/background.jpg";
    String backgroundDir2 = "resources/frame/scr1.jpg";
    String backgroundDir3 = "resources/frame/scr2.jpg";

    BufferedImage backimg1 = ImageIO.read(new File(backgroundDir1));
    BufferedImage backimg2 = ImageIO.read(new File(backgroundDir2));
    BufferedImage backimg3 = ImageIO.read(new File(backgroundDir3));

    backgroundL = new JLabel(new ImageIcon(backimg1));
    Thread.sleep(2000);
    setbackground();
    setVisible(true);
    backgroundL = new JLabel(new ImageIcon(backimg2));
    setbackground();
    setVisible(true);
    Thread.sleep(2000);
    bakckgroundL = new JLabel(new ImageIcon(backimg3));
    setbackground();
    setVisible(true);

    if(backimg != null) {
         backgroundL = new JLabel(new ImageIcon(backimg));;
        }
    }
    busy = false;
}//end of loading screen

Solution 1:

See ImageViewer for a working example of displaying images using a Swing based Timer.

See also How to use Swing Timers.


And while I'm here, another (prettier) example of animating an image. It uses this Mercator map of land masses. The image can be tiled horizontally, and therefore be scrolled left/right as needed.

Mercator Map of Land Masses

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import javax.swing.*;

import java.net.URL;
import javax.imageio.ImageIO;

public class WorldView {

    public static void main(String[] args) throws Exception {
        URL url = new URL("http://i.stack.imgur.com/P59NF.png");
        final BufferedImage bi = ImageIO.read(url);
        Runnable r = new Runnable() {

            @Override
            public void run() {
                int width = 640;
                int height = 316;
                Graphics2D g = bi.createGraphics();

                float[] floats = new float[]{0f, .4f, .55f, 1f};
                Color[] colors = new Color[]{
                    new Color(20, 20, 20, 0),
                    new Color(0, 10, 20, 41),
                    new Color(0, 10, 20, 207),
                    new Color(0, 10, 20, 230),};
                final LinearGradientPaint gp2 = new LinearGradientPaint(
                        new Point2D.Double(320f, 0f),
                        new Point2D.Double(0f, 0f),
                        floats,
                        colors,
                        MultipleGradientPaint.CycleMethod.REFLECT);

                final BufferedImage canvas = new BufferedImage(
                        bi.getWidth(), bi.getHeight() + 60,
                        BufferedImage.TYPE_INT_RGB);

                final JLabel animationLabel = new JLabel(new ImageIcon(canvas));
                ActionListener animator = new ActionListener() {

                    int x = 0;
                    int y = 30;

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Graphics2D g = canvas.createGraphics();
                        g.setColor(new Color(55, 75, 125));

                        g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());

                        int offset = (x % bi.getWidth());
                        g.drawImage(bi, offset, y, null);
                        g.drawImage(bi, offset - bi.getWidth(), y, null);

                        g.setPaint(gp2);
                        g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());

                        g.dispose();

                        animationLabel.repaint();

                        x++;
                    }
                };
                Timer timer = new Timer(40, animator);
                timer.start();
                JOptionPane.showMessageDialog(null, animationLabel);
                timer.stop();
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency
        SwingUtilities.invokeLater(r);
    }
}

Here is a version of that image with the equator added (it is 44 pixels 'south' of the center of the image).

enter image description here

Solution 2:

You're calling Thread.sleep(...) and likely on the EDT or Swing event thread (full name is the Event Dispatch Thread). This thread is responsible for all Swing painting/drawing and user interactions, and so sleeping it will only serve to freeze your entire GUI. Instead you should use a Swing Timer to allow you to swap a JLabel's ImageIcon.

So, briefly:

  • Don't call Thread.sleep(...) on the Swing event thread (Event Dispatch Thread or EDT).
  • Do use a Swing Timer to do your repeating delayed actions.
  • Don't make and add many JLabels. Just make and add one.
  • Do Swap the ImageIcon that the JLabel displays by calling setIcon(...) on the label.
  • Better (cleaner) to write if (busy == false) { as if (!busy) {

e.g.,

ImageIcon[] icons = {...}; // filled up with your ImageIcons

if (!busy) {
  int timerDelay = 2000;
  new Timer(timerDelay, new ActionListener() {
    private int i = 0;
    public void actionPerfomed(ActionEvent e) {
      myLabel.setIcon(icons(i));
      i++;
      if (i == icons.length) {
        ((Timer)e.getSource).stop();
      } 
    };
  }).start();
}