如何在运行时更改JFrame中的JPanel?

48

简单地说,有一个简单的Java Swing应用程序,其中包括一些组件的JFrame。其中一个组件是一个JPanel,旨在在用户操作时被另一个JPanel替换。

那么,正确的做法是什么?我已经尝试过:

panel = new CustomJPanelWithComponentsOnIt();
parentFrameJPanelBelongsTo.pack();

但这种方法行不通,你有什么建议吗?

14个回答

53
您的使用场景似乎非常适合使用CardLayout
在卡片布局中,您可以将多个面板添加到同一位置,但是每次只能显示或隐藏一个面板。

4
使用CardLayout时,请记住使用它的面板的首选大小将是布局中最大面板的大小。 如果您希望回收空间,则需要使用setVisible(false)和setPreferredSize(new Dimension(0,0))隐藏组件。 - Joseph Gordon
我之前没有听说过CardLayout。谢谢 - 它非常适合我需要做的事情。 - I82Much
是的,这是正确的方式。也许是最好的。 - WesternGun

29

1)设置第一个面板:

JFrame frame=new JFrame();
frame.getContentPane().add(new JPanel());

2)更换面板:

frame.getContentPane().removeAll();
frame.getContentPane().add(new JPanel());

还要注意必须在事件线程中执行此操作,为确保这一点,请使用SwingUtilities.invokeLaterSwingWorker


23
frame.setContentPane(newContents());
frame.revalidate(); // frame.pack() if you want to resize.

请记住,Java 使用“按值传递副本引用”的参数传递方式。因此,更改一个变量不会更改传递给其他方法的引用的副本。

另外,请注意JFrame 的名称在可用性方面非常令人困惑。添加组件或设置布局(通常)会对内容窗格执行操作。令人奇怪的是,获取布局实际上确实会给您框架的布局管理器。


Tom,感谢您的回复。我不是想替换contentpane,只是想在其上放置一个jpanel(就像frame.add(jpanel1),frame.add(jpanel2),frame.add(jpanel3))。您能否提供一种合理的代码解决方案? - yanchenko
嗨 John,你的使用情况似乎非常适合使用 CardLayout。http://java.sun.com/docs/books/tutorial/uiswing/layout/card.html在卡片布局中,您可以将多个面板添加到同一位置,但是一次只能显示或隐藏一个面板。 - Swapnonil Mukherjee
2
使用setContentPane()方法时可能会遇到问题。我曾经遇到过这样的情况,在调用该方法后所有(自定义)光标都不再显示。 - xastor

8
希望这段代码能让您了解如何在JFrame中更改jPanels。
public class PanelTest extends JFrame {

        Container contentPane;

        public PanelTest()  {
           super("Changing JPanel inside a JFrame");
           contentPane=getContentPane();
        }

        public void createChangePanel() {
           contentPane.removeAll();
           JPanel newPanel=new JPanel();
           contentPane.add(newPanel);
           System.out.println("new panel created");//for debugging purposes
           validate();
           setVisible(true);
        }
}

5

关于用户操作:

// 你需要做一些类似以下的事情

myJFrame.getContentPane().removeAll()
myJFrame.getContentPane().invalidate()

myJFrame.getContentPane().add(newContentPanel)
myJFrame.getContentPane().revalidate()

然后您可以根据需要调整窗口大小。

4
Game game =  new Game();
getContentPane().removeAll(); 
setContentPane(game);  
getContentPane().revalidate(); //IMPORTANT
getContentPane().repaint();    //IMPORTANT

1
嗨,Adnane Afifi,请考虑为您的解决方案添加一些说明。 - kplus

3
这取决于它的使用方式。如果需要在这两个面板之间来回切换,则使用CardLayout。如果只需一次从第一个面板切换到第二个面板(不返回第一个面板),则建议使用telcontar的建议进行替换。但是,如果JPanel不是框架中唯一的元素,则应该使用remove(java.awt.Component)而不是removeAll。
如果你处于这两种情况之间,那么就是时间和空间的权衡。CardLayout会节省时间,但会占用更多的内存,因为必须始终在内存中保存整个面板。但是,如果您仅在需要时替换面板并按需构建,则无需保留该内存,但切换需要更多时间。
另外,您可以尝试使用JTabbedPane来使用选项卡(它甚至比CardLayout更容易,因为它自动处理显示/隐藏)。

1

我也遇到了完全相同的问题!不可思议!!我找到的解决方案是:

  1. 将所有组件(JPanels)添加到容器中;
  2. 对它们所有都使用setVisible(false)方法;
  3. 在用户操作时,将要显示的面板设置为setVisible(true)。
// Hiding all components (JPanels) added to a container (ex: another JPanel)
for (Component component : this.container.getComponents()) {
      component.setVisible(false);
}

// Showing only the selected JPanel, the one user wants to see
panel.setVisible(true);

不需要revalidate()、validate()或CardLayout。


1

layout.replace() 方法只适用于 GroupLayout 管理器。


其他布局管理器(如CardLayout、BoxLayout等)不支持此功能,但需要您首先调用RemoveLayoutComponent()方法,然后再调用AddLayoutComponent()方法。 :-) [仅纠正记录]


1
其他人已经回答了这个问题。我想建议你使用JTabbedPane代替替换内容。通常情况下,让应用程序的视觉元素消失或被其他内容替换是不好的。当然,每个规则都有例外,只有你和你的用户社区可以决定最佳方法。

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