Search code examples
javaswingimageiconjtabbedpane

Changing Icon not causing repaint on JTabbedPane


I have a strange issue. I have a JTabbedPane which has several JPanels added as children.

One of these panels has an ImageIcon along with a label. The actual Image used by this ImageIcon is being changed periodically based on what is happening in the associated JPanel.

Here is an example of what I mean: enter image description here

Certain events in the second tab cause the red icon to change.

When these events happen I change the Image using the following method: http://docs.oracle.com/javase/7/docs/api/javax/swing/ImageIcon.html#setImage(java.awt.Image)

However after I have set the image, the actual tab is not repainting. It is only repainting on other events (such as mouse over or click).

I would have thought that changing an icons image would cause it to trigger a repaint on anything that is using that icon? Is there some trick to getting this to work?

I could probably implement a hacky solution by creating a custom icon class and passing a JComponent to it which will be repainted when the image is changed, however this would bring up another problem of repainting the entire JTabbedPane every time the icon changes OR calculating the area the icon is occupying and repainting that (which seems like a lot of effort).

EDIT:

Here is a simple application which demonstrates my problem. Assuming two images located in the src root directory named Image1.png and Image2.png.

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

public class JTabbedPaneTest {

    JFrame jFrame;

    JTabbedPane jTabbedPane;
    ImageIcon testIcon;

    BufferedImage image1;
    BufferedImage image2;

    Timer timer;

    public JTabbedPaneTest() throws IOException {

        jFrame = new JFrame();
        jFrame.setMinimumSize(new Dimension(300, 300));

        image1 = ImageIO.read(getClass().getResource("/Image1.png"));
        image2 = ImageIO.read(getClass().getResource("/Image2.png"));

        testIcon = new ImageIcon(image1);

        jTabbedPane = new JTabbedPane();
        jTabbedPane.addTab("Tab 1", testIcon, new JPanel());
        jTabbedPane.addTab("Tab 2", new JPanel());

        jFrame.add(jTabbedPane, BorderLayout.CENTER);

        timer = new Timer(0, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Changing image...");
                if(testIcon.getImage() == image1){
                    testIcon.setImage(image2);
                }
                else {
                    testIcon.setImage(image1);
                }
            }
        });

        timer.setRepeats(true);
        timer.setDelay(1000);
        timer.start();
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    JTabbedPaneTest application = new JTabbedPaneTest();
                    application.jFrame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.err.println(e.getClass().getSimpleName() + " : " + e.getMessage());
                    System.exit(1);
                }
            }
        });
    }

}

The tab itself is not repainted automatically and requires another event to trigger the painting.


Solution

  • If it's not painted then call repaint() after changing the icon.

        public void actionPerformed(ActionEvent e) {
                System.out.println("Changing image...");
                if (testIcon.getImage() == image1) {
                    testIcon.setImage(image2);
                } else {
                    testIcon.setImage(image1);
                }
                jTabbedPane.repaint();//calling repaint after icon change
            }