Show an animated BG in Swing

animated-gif, background, image, java, swing

Solution

Using `ImageIcon` is probably the most straightforward thing to do. A couple of things to keep in mind:

`ImageIcon(URL)` itself makes use of `Toolkit.getImage(URL)`. You may prefer using `Toolkit.createImage(URL)` instead - `getImage()` may use cached or shared image data.

`ImageIcon` makes use of a `MediaTracker` to effectively wait until the image is completely loaded.

So, your issue may not be the use of `Toolkit` (`ImageIO` is a different beast), but rather the fact that you're not rendering a fully loaded image. One interesting thing to try would be:

Image image = f.getToolkit().createImage(url);
//...
ImagePanel imagePanel = new ImagePanel(image);
imagePanel.prepareImage(image, imagePanel);
//...

My Swing/AWT/J2D may be a bit fuzzy, but the idea is that since your `ImagePanel` is an `ImageObserver`, it can be notified asynchronously about image information. The `Component.imageUpdate()` method should invoke `repaint` as needed.

Edit:

As noted in the comments, the call to `prepareImage` is not required - a working example is included below. The key is that the overridden `paintComponent` method invokes `Graphics.drawImage`, which provides the `ImageObserver` hook. The `imageUpdate` method (implemented in `java.awt.Component`) will continuously be invoked with the `ImageObserver.FRAMEBITS` flag set.

import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.image.ImageObserver;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class ImagePanel extends JPanel {

    private final Image image;

    public ImagePanel(Image image) {
        super();
        this.image = image;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(this.image, 0, 0, getWidth(), getHeight(), this);
    }

    public static void main(String[] args) throws MalformedURLException {
        final URL url = new URL("https://i.stack.imgur.com/iQFxo.gif");
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame f = new JFrame("Image");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setLocationByPlatform(true);

                Image image = f.getToolkit().createImage(url);
                ImagePanel imagePanel = new ImagePanel(image);
                imagePanel.setLayout(new GridLayout(5, 10, 10, 10));
                imagePanel.setBorder(new EmptyBorder(20, 20, 20, 20));
                for (int ii = 1; ii < 51; ii++) {
                    imagePanel.add(new JButton("" + ii));
                }

                f.setContentPane(imagePanel);
                f.pack();
                f.setVisible(true);
            }
        });
    }
}

Problem

An animated (cycling) GIF can be displayed in a `JLabel` or in HTML (in formatted text components like `JEditorPane`) and be seen to cycle. But to load an image to paint as the background of a container, I would typically use either `ImageIO.read()` or `Toolkit.getImage()` (the latter when I'm feeling nostalgic for the last millennium). Neither method of loading an image results in a cycling image, it is typically just the first frame. How to load an animated image for a background? E.G.

Original source