Java 更改图标不会导致在JTabbedPane上重新绘制

Java 更改图标不会导致在JTabbedPane上重新绘制,java,swing,imageicon,jtabbedpane,Java,Swing,Imageicon,Jtabbedpane,我有一个奇怪的问题。我有一个JTabbedPane,其中有几个作为子级添加的JPanel 其中一个面板有一个图像图标和一个标签。此ImageIcon使用的实际图像将根据相关JPanel中发生的情况定期更改 以下是我的意思的一个例子: 第二个选项卡中的某些事件会导致红色图标更改 当这些事件发生时,我使用以下方法更改图像: 但是,在我设置了图像之后,实际的选项卡并没有重新绘制。它仅在其他事件(如鼠标悬停或单击)上重新绘制 我原以为更改图标图像会导致它触发对使用该图标的任何对象的重新绘制?有什么诀

我有一个奇怪的问题。我有一个JTabbedPane,其中有几个作为子级添加的JPanel

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

以下是我的意思的一个例子:

第二个选项卡中的某些事件会导致红色图标更改

当这些事件发生时,我使用以下方法更改图像:

但是,在我设置了图像之后,实际的选项卡并没有重新绘制。它仅在其他事件(如鼠标悬停或单击)上重新绘制

我原以为更改图标图像会导致它触发对使用该图标的任何对象的重新绘制?有什么诀窍可以让它发挥作用吗

我可能会通过创建一个自定义图标类并向其传递一个JComponent来实现一个黑客解决方案,当图像发生更改时,JComponent将被重新绘制,但是这会带来另一个问题,即每次图标更改时重新绘制整个JTabbedPane,或者计算图标占用的区域并重新绘制该区域(这似乎需要很大的努力)

编辑:

下面是一个简单的应用程序,它演示了我的问题

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

}

选项卡本身不会自动重新绘制,需要另一个事件来触发绘制。

如果未绘制,请在更改图标后调用
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
        }
使用
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);
                }
            }
        });
    }

}

<>顺便说一下,使用<代码> SWIVITULTIONY调用No.*/Cult>方法是很好的实践。

对我来说很好。考虑提供一个演示你的问题的代码。这不是一个代码转储,而是你正在做的一个例子,它突出了你所面临的问题。这将导致更少的混乱和更好的响应:你使用<代码>秋千。实用程序。调用*方法设置图像?在这里效果很好,可能您使用的L&F有问题。请使用系统L&F而不是自定义的L&F进行测试。大家好,感谢您的回复!我尝试关闭了我正在使用的物质L&F,但问题仍然存在。我将在接下来的几分钟内发布一个代码示例如果SwingUtilities.invoke不起作用,则使用分钟。只是尝试使用SwingUtilities.invokeLater并删除L&F,但问题仍然存在。我现在将尝试制作一个非常简单的演示。我有一个这样的版本,它可以起作用,但这意味着我需要保留对图标使用位置的引用,这是我希望避免的。