Swing JPanel不会重绘

11

我有一个简单的对象,它扩展了 JPanel。当在此对象上调用 update() 方法时,它应该向面板添加一些标签,然后重绘。但是,在调用更新方法后,标签不会显示出来。下面是更新的代码:

public void update(){
        GridBagConstraints constraints = new GridBagConstraints();

        if(cardsHidden){
            for(int i = 0; i < 2; i++){
                constraints.gridx = i;
                constraints.gridy = 0;
                JLabel card = new JLabel(PlayingCards.cardImages[PlayingCards.CARD_BACK_INDEX]);
                add(card, constraints);
            }
        }
        else{
            Card[] holeCards = player.getHoleCards();
            for(int i = 0; i < holeCards.length; i++){
                constraints.gridx = i;
                constraints.gridy = 0;
                JLabel card = new JLabel(holeCards[i].getImageIcon());
                add(card, constraints); 
            }
        }

        validate();
        repaint();
    }

有什么想法吗?

谢谢。

编辑

问题已解决:

事实证明,HoleCardsPanel 没有正确地添加到其父框架中。一旦修复了这个问题,添加新的 JLabel 就正常工作了。我还:

  • 使用 SwingUtilities.invokeLater 在事件分派线程中调用了 update() 方法
  • 必须从最上层组件(在这种情况下是 JFrame)中调用 validate(),如 Devon_C_Miller 在他的回答中建议的那样
6个回答

25

这取决于您希望发生什么以及使用了哪些布局管理器,但基本规则如下:

  1. 确保在EDT上调用update。如果不是(SwingUtilities.isEventDispatchThread()返回false),则需要使用SwingUtilities.invokeLater在EDT上安排更新。例如:

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            update();
        }});
    
  2. 调用invalidate()方法。大多数更改组件的操作都会自动执行此操作,只有在这些更改无法自动执行时才需要手动调用。

  3. 在受影响的最高级别组件上调用validate()方法。这可能是 Java 渲染循环中最困难的部分之一。invalidate方法将标记组件及其所有祖先需要重新布局,而validate方法则会对组件及其所有后代进行布局。其中一个方法从下向上工作,而另一个方法从上向下工作。您需要在受更改影响的树中最高的组件上调用validate方法。

    此外,在顶级组件(如 JWindow、JDialog、JFrame)上调用validate方法不一定会调整该组件的大小。要实现这一点,您需要调用pack()setSize()方法。

  4. 如果更改导致容器的大小或位置发生变化,则调整大小的容器将重绘,但它们不会擦除所占用的空间。在容器的父级别上调用repaint()方法将导致它重新绘制背景,从而修正此问题。


1
我在哪里可以找到一个好的资源来讨论所有这些规则?Javadoc并没有对你上面写的一切都有明确的解释。肯定有更好的资源吧…? - Gili

17

尝试调用revalidate(); 重绘(repaint)并非你想要的操作。

根据:

API文档

注意:如果将组件添加到已显示的容器中,则必须在该容器上调用validate以显示新组件。如果正在添加多个组件,则可以通过在所有组件添加完成后仅调用一次validate来提高效率。

revalidate()基本上是invalidate()后跟着validate()。

请参见问题.....


我刚刚尝试删除repaint()语句并将validate()更改为revalidate(),但没有成功 :( - Aly
你是从另一个线程还是事件分派线程调用更新函数? - Chris Kannon
我正在从运行在标准线程上的另一个对象直接调用更新。 - Aly
哦,再说一句...在拥有JPanel的JFrame上调用revalidate!我刚想到这个。 - Chris Kannon
JFrame没有'revalidate()'方法。 - Aly
对于JFrame,使用validate是正确的。如果这不起作用,请先尝试invalidate。 - Chris Kannon

5

2
您可以尝试调用updateUI()方法。

0

你尝试过在标签上使用SetVisible()吗?

你尝试过在初始化时添加对象而不进行任何更新吗?如果它们在那里没有显示出来,它们永远不会显示出来。


好的。就我个人而言,我不同意这个负评。人们通常会忘记这样简单的事情,而这可能会解决其他人的问题。 - Philippe Carriere
Java组件在创建时默认不可见吗? - Chris Kannon
是的。但我不知道剩下的代码。当有人提到一个组件没有显示出来时,setvisible就会出现在我的脑海中。对于寻找答案的95%的人来说,它可能不是解决问题的方法,但对于其他5%的人来说,他们来到这个网站,看到这个问题,它将解决问题。如果我说“你确定你的屏幕打开了吗?”我会同意一个反对票 ;) - Philippe Carriere
不是说这个踩票是值得的,只是他明显在上面的代码中添加了控件,因为他刚刚创建它们,所以它们必须设置为setVisible(true)。 - Chris Kannon

0
原来是 HoleCardsPanel 没有正确地添加到其父帧中,一旦修复了这个问题,新的 JLabels 的添加就没问题了。我使用 SwingUtilities.invokeLater 事件调度线程 中添加了对 update() 方法的调用。此外,我还需要从最上层组件(在本例中是 JFrame )调用 validate() 方法。

1
考虑将这些结果添加到您的问题中,以备将来参考。 - trashgod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接