Java的validate()方法无法正常工作

3
我有一个JFrame,里面有一个JPanel作为内容面板。

基本上,我使用JPanel在单击时加载内容。新内容也返回为JPanel,因此最终是JPanel->内部JPanel->内部JFrame。当我需要加载新内容时,我清除面板,加载新内容,并验证(validate)JFrame和JPanel,新内容就会显示出来。

我的问题是,当新内容显示时,很明显验证方法起作用了,因为我可以看到新的界面,但我也可以看到旧的界面,就好像它变成了背景一样;我可以调整窗口大小,它就消失了,看起来就像应该的样子。

enter image description here enter image description here

这只是validate的工作方式,还是我可以修复它?

编辑:这个方法有效。问题是我没有手动调用repaint。

public BaseWindow setContent(JComponent comp){
    contentPane.add(comp);
    contentPane.revalidate();
    contentPane.repaint();
    return this;
}
6个回答

4

通常从面板中添加/删除一个或两个组件的代码如下:

panel.remove(..);
panel.add(...);
panel.revalidate();
panel.repaint(); // sometimes needed

然而,如果您要替换面板上的所有组件,则更好的方法是使用卡片布局

您已经说明了revaliate()后跟着repaint()不起作用,所以我唯一的建议是发布一个SSCCE来演示问题。


2

不要使用validate。改用revalidate()

revalidate()首先调用invalidate(),然后调用validate()。 在Swing中,您很少使用validate()。

注意:我还觉得可能旧面板没有被清除/删除。再检查一下!


顺便提一句,我实际上已经尝试过这样做了,虽然我在最初的问题中没有提到。我也尝试了手动执行无效化(invalidate)后跟验证(validate)。 - slex
你确定那些组件真的被移除了吗?能否打印组件树并尝试检查它们是否真的被移除了。我非常确定它们没有被移除。 - Suraj Chandran
我现在没有机会,直到周一才能处理,但我相信它们是正确的,因为当我稍微调整屏幕大小时,它们就消失了。 - slex

1

Validate() 的作用是根据您安装的布局管理器使组件重新排列自己。这并不是您应该使用的。

我看不到您的代码,所以我不确定您到底在做什么。我可以猜测在“内部面板”上调用repaint()将解决您遇到的问题...但实际上,如果您做事情正确,就不需要调用repaint()或validate()。

创建两个JPanels,一个带有内容A(例如您的按钮),另一个带有内容B(例如您的“静态”字段)。使用父容器(JFrame的内容窗格?)上的“add()”和“remove()”方法相互交换这两个JPanels,以便在帧的那一部分切换显示的内容。

然后您就不需要做任何其他事情;它应该可以正常工作。


基本上我已经在做了。我使用removeAll()方法来清除JPannel(默认情况下在我的JFrame中并用作内容窗格),然后使用add()方法从我的“屏幕”类之一(即ChoiceScreen/ScreenWithStaticButton)添加内容的JPannel。 - slex
这两个JPanels都是不透明的吗?(setOpaque(true)) 同时,你尝试过在将你的面板从中移除并添加到容器后调用 repaint() 方法吗?看起来容器没有被正确地触发重绘自身,但如果是这种情况,重绘容器应该足够轻松地解决问题。虽然这样做不是必要的,因为add()和remove()会自动触发重绘。 - Xanatos
如果您发布一些代码(尽可能简洁),那么我们可以更容易地提出建议。 - Xanatos

0
这里有另一个可能的解决方案:
同时将两个JPanels放在一起,侧边排列,然后确保任何时候只有一个可见:
JPanel p = new JPanel(new BorderLayout());
p.add( panelA, BorderLayout.EAST );
p.add( panelB, BroderLayout.WEST );
panelA.setVisible(true);
panelB.setVisible(false);

然后当用户点击按钮切换面板时:

panelA.setVisible(false);
panelB.setVisible(true);

setVisible() 方法和 BorderLayout 应该为您处理验证、布局和调用 repaint()。


“-1,那不是解决方案,那是一个hack。Swing支持动态添加/删除组件,因此无需诉诸hack。问题出在海报的代码上。” - camickr
@camickr:请解释一下为什么这段代码被称为“hack”?它运行良好,不依赖于任何神秘或未记录的行为,没有副作用,在面对变化时非常健壮,并且清晰易懂。有选择地显示和隐藏组件是setVisible()的完全合法用途...我不知道为什么要删除组件而不是隐藏它们。 - Xanatos
在某些情况下,使用可见性是合理的。在这种情况下,OP想要替换整个面板。示例仅涉及两个面板。您的建议是使用东部和西部来容纳这些面板。但是,如果应用程序需要交换多个面板?显然,您的建议无法实现。即使您使用不同的布局管理器,仍然会有问题,因为它不是简单的使一个可见,使一个不可见。更好的设计是为每个功能设置一个面板,并根据功能交换面板。 - camickr
这只是针对其他类似于OP试图解决的问题(可以说)不太理想的解决方案。这并不意味着它是一种“hack”,或者在所有情况下都是一个差劲的解决方案。(尽管我承认,对于更复杂的GUI,添加/删除确实比setVisible更好,原因就是你所指出的。) - Xanatos
我将此投票返回中立状态;我认为,虽然这不是我的解决方案,但对于另一个遇到类似问题的读者来说,由于愚蠢的-1,他可能会放弃这个解决方案。 - slex

0

我最终通过更改添加/删除的面板来解决了我的问题(显示未显示,按钮会保持点击/未取消选择)。

问题:

frame.removeAll();
frame.add(getNewPanelDisplay());
frame.revalidate();
frame.repaint();

解决方案:

//initializer()
mainPanel = new JPanel();
frame.add(mainPanel());
// ...
//update()
mainPanel.remove(0);
mainPanel.add(getTablePanel(), 0);
frame.revalidate();
frame.repaint();

0

我不确定validate()方法是否承诺完全重绘容器。你可能需要自己调用repaint()方法来使其按照你的意愿工作。


错误!validate()逻辑内部最终触发repaint。 - Suraj Chandran
@Suraj:实际上,只有在容器首先被invalidate()后,validate()才会最终触发repaint()。因此,您可以调用revalidate(),或invalidate()+validate()。但这是一种非常低效的间接调用repaint()的方式。 - Xanatos
@Xantos 但唉!repaint() 无法解决他的问题。 - Suraj Chandran

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