【问题标题】:Changing Icon not causing repaint on JTabbedPane更改图标不会导致 JTabbedPane 上的重绘
【发布时间】:2015-10-13 06:45:26
【问题描述】:

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

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

这是我的意思的一个例子:

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

当这些事件发生时,我使用以下方法更改图像: 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);
                }
            }
        });
    }

}

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

【问题讨论】:

  • 对我来说很好。考虑提供一个runnable example 来证明您的问题。这不是代码转储,而是您正在做的事情的一个例子,它突出了您遇到的问题。这将减少混乱并获得更好的响应
  • 你是用SwingUtilities.invoke*的方法设置图片吗?
  • 这里运行良好,可能是您使用的 L&F 有问题。尝试使用系统 L&F,而不是使用自定义系统进行测试。
  • 大家好,感谢您的回复!我尝试关闭我正在使用的 Substance L&F,但问题仍然存在。如果 SwingUtilities.invoke 不起作用,我将在接下来的几分钟内发布代码示例。
  • 刚刚尝试使用 SwingUtilities.invokeLater 并删除 L&F,但问题仍然存在。我现在尝试做一个非常简单的演示。

标签: java swing imageicon jtabbedpane


【解决方案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
        }

【讨论】:

  • 我有一个这样的版本,它可以工作,但这意味着我需要保留对使用图标的任何位置的引用,这是我希望避免的。
【解决方案2】:

使用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* 方法是一种很好的做法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多