Show an animated BG in Swing
Asked Answered
N

3

10

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.

Nammu answered 31/5, 2012 at 15:40 Comment(0)
R
14

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);
            }
        });
    }
}
Runoff answered 31/5, 2012 at 18:57 Comment(3)
Great catch. Tested Toolkit.createImage(URL) as a direct 'drop-in' replacement for new ImageIcon(url).getImage() & the image also cycled. Note that I did not bother to call prepareImage.Nammu
I'll edit my response tomorrow, but I also noticed that the test I created (a modified version of your code) did not require the prepareImage call. I believe that the call to Graphics.drawImage provides the ImageObserver hook...Runoff
Only noticed the edit just now. I thought this could be my 1st canonical Q&A, but it turns out your answer is better! I'm not sure whether to be irritated at being trumped, or delighted at finding someone with more details. (considers a few moments more..) OK I'm delighted. Thanks for your answer. :)Nammu
N
16

To get a cycling (animated) GIF for custom painting, one trick is to load it using an ImageIcon. While the image returned from either of the two methods listed in the question is static, one obtained from an ImageIcon is animated.

The code below will add 50 buttons, then soon after the frames of the 'zooming stars' animated GIF1 as BG to them. The ImagePanel will stretch the image to the size of the panel.

  1. It is based on this image.

Zooming Stars GIF

import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.net.URL;

class ImagePanel extends JPanel {

    private Image image;

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

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

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

                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);
            }
        });
    }
}
Nammu answered 31/5, 2012 at 15:40 Comment(1)
Thanks for this neat SSCCE, I was unaware that ImageIcon was able to display the animation of a .gif. I was debating whether I should add an animated picture to my GUI and now I know that it will be rather simple if I want to add it.Extern
R
14

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);
            }
        });
    }
}
Runoff answered 31/5, 2012 at 18:57 Comment(3)
Great catch. Tested Toolkit.createImage(URL) as a direct 'drop-in' replacement for new ImageIcon(url).getImage() & the image also cycled. Note that I did not bother to call prepareImage.Nammu
I'll edit my response tomorrow, but I also noticed that the test I created (a modified version of your code) did not require the prepareImage call. I believe that the call to Graphics.drawImage provides the ImageObserver hook...Runoff
Only noticed the edit just now. I thought this could be my 1st canonical Q&A, but it turns out your answer is better! I'm not sure whether to be irritated at being trumped, or delighted at finding someone with more details. (considers a few moments more..) OK I'm delighted. Thanks for your answer. :)Nammu
C
1

I managed to get the animated gif inside my JPanel this way:

private JPanel loadingPanel() {
    JPanel panel = new JPanel();
    BoxLayout layoutMgr = new BoxLayout(panel, BoxLayout.PAGE_AXIS);
    panel.setLayout(layoutMgr);

    ClassLoader cldr = this.getClass().getClassLoader();
    java.net.URL imageURL   = cldr.getResource("img/spinner.gif");
    ImageIcon imageIcon = new ImageIcon(imageURL);
    JLabel iconLabel = new JLabel();
    iconLabel.setIcon(imageIcon);
    imageIcon.setImageObserver(iconLabel);

    JLabel label = new JLabel("Loading...");
    panel.add(iconLabel);
    panel.add(label);
    return panel;
}

Some points of this approach:
1. The image file is within the jar;
2. ImageIO.read() returns a BufferedImage, which doesn't update the ImageObserver;
3. Another alternative to find images that are bundled in the jar file is to ask the Java class loader, the code that loaded your program, to get the files. It knows where things are.

So by doing this I was able to get my animated gif inside my JPanel and it worked like a charm.

Counter answered 1/3, 2013 at 20:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.