更改图标不会导致JTabbedPane重绘

时间:2015-07-23 06:00:45

标签: java swing imageicon jtabbedpane

我有一个奇怪的问题。我有一个JTabbedPane,有几个JPanels作为孩子添加。

其中一个面板有一个ImageIcon和一个标签。此ImageIcon使用的实际图像将根据相关JPanel中发生的情况定期更改。

这是我的意思的一个例子: enter image description here

第二个标签中的某些事件会导致红色图标发生变化。

当这些事件发生时,我使用以下方法更改Image: http://docs.oracle.com/javase/7/docs/api/javax/swing/ImageIcon.html#setImage(java.awt.Image)

但是,在设置图像后,实际的标签不会重新绘制。它仅重新绘制其他事件(例如鼠标悬停或单击)。

我原以为更改图标图像会导致它在使用该图标的任何内容上触发重绘?是否有一些技巧可以让它发挥作用?

我可能通过创建一个自定义图标类并将一个JComponent传递给它来实现一个hacky解决方案,这将在图像更改时重新绘制,但是这会带来另一个问题,即每次图标更改时重新绘制整个JTabbedPane计算图标占据的区域并重新绘制(这看起来很费劲)。

编辑:

这是一个简单的应用程序,它演示了我的问题。假设两个图像位于src根目录中,名为Image1.png和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);
                }
            }
        });
    }

}

标签本身不会自动重新绘制,需要另一个事件来触发绘画。

2 个答案:

答案 0 :(得分:2)

如果没有绘制,请在更改图标后调用repaint()

    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
        }

答案 1 :(得分:0)

使用JTabbedPane#setIconAt设置新图标。下面有一个修改后的来源。

package ant.test;
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 testIcon1;
    ImageIcon testIcon2;

    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"));

        testIcon1 = new ImageIcon(image1);
        testIcon2 = new ImageIcon(image2);

        jTabbedPane = new JTabbedPane();
        jTabbedPane.addTab("Tab 1", testIcon1, 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(jTabbedPane.getIconAt(0) == testIcon1){
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override public void run() {
                            jTabbedPane.setIconAt(0, testIcon2);
                        }
                    });
                }
                else {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override public void run() {
                            jTabbedPane.setIconAt(0, testIcon1);
                        }
                    });
                }
            }
        });

        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);
                }
            }
        });
    }

}

顺便说一下,使用SwingUtilities#invoke*方法是一种很好的做法。