简体   繁体   English

如何动态更改 JFrame 内的 JPanel?

[英]How do I change JPanel inside a JFrame on the fly?

To put it simple, there's a simple java swing app that consists of JFrame with some components in it.简单来说,有一个简单的 java swing 应用程序,它由 JFrame 和一些组件组成。 One of the components is a JPanel that is meant to be replaced by another JPanel on user action.其中一个组件是一个 JPanel,旨在根据用户操作由另一个 JPanel 替换。

So, what's the correct way of doing such a thing?那么,做这种事情的正确方法是什么? I've tried我试过了

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

but this won't work.但这行不通。 What would you suggest?你有什么建议?

Your use case, seems perfect for CardLayout .您的用例似乎非常适合CardLayout

In card layout you can add multiple panels in the same place, but then show or hide, one panel at a time.在卡片布局中,您可以在同一位置添加多个面板,然后一次显示或隐藏一个面板。

1) Setting the first Panel: 1)设置第一个面板:

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

2)Replacing the panel: 2)更换面板:

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

Also notice that you must do this in the Event's Thread, to ensure this use the SwingUtilities.invokeLater or the SwingWorker另请注意,您必须在事件的线程中执行此操作,以确保使用SwingUtilities.invokeLaterSwingWorker

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

Remember, Java use 'copy reference by value' argument passing.请记住,Java 使用“按值复制引用”参数传递。 So changing a variable wont change copies of the reference passed to other methods.因此,更改变量不会更改传递给其他方法的引用副本。

Also note JFrame is very confusing in the name of usability.还要注意JFrame在可用性方面非常混乱。 Adding a component or setting a layout (usually) performs the operation on the content pane.添加组件或设置布局(通常)在内容窗格上执行操作。 Oddly enough, getting the layout really does give you the frame's layout manager.奇怪的是,获取布局确实为您提供了框架的布局管理器。

Hope this piece of code give you an idea of changing jPanels inside a JFrame.希望这段代码能让您了解如何在 JFrame 中更改 jPanel。

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

On the user action:在用户操作上:

// you have to do something along the lines of // 你必须按照以下方式做一些事情

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

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

Then you can resize your wndow as needed.然后您可以根据需要调整窗口大小。

It all depends on how its going to be used.这一切都取决于它将如何使用。 If you will want to switch back and forth between these two panels then use a CardLayout.如果您想在这两个面板之间来回切换,请使用 CardLayout。 If you are only switching from the first to the second once and (and not going back) then I would use telcontar s suggestion and just replace it.如果您只从第一个切换到第二个并且(并且不回去),那么我会使用telcontar的建议并替换它。 Though if the JPanel isn't the only thing in your frame I would use remove(java.awt.Component) instead of removeAll.虽然如果 JPanel 不是框架中唯一的东西,我会使用remove(java.awt.Component)而不是 removeAll。

If you are somewhere in between these two cases its basically a time-space tradeoff.如果您介于这两种情况之间,则它基本上是一个时空权衡。 The CardLayout will save you time but take up more memory by having to keep this whole other panel in memory at all times. CardLayout 将节省您的时间,但由于必须始终将整个其他面板保留在内存中,因此会占用更多内存。 But if you just replace the panel when needed and construct it on demand, you don't have to keep that meory around but it takes more time to switch.但是,如果您只是在需要时更换面板并按需构建它,您就不必保留这些记忆,但切换需要更多时间。

Also you can try a JTabbedPane to use tabs instead (its even easier than CardLayout because it handles the showing/hiding automitically)你也可以尝试使用 JTabbedPane 来代替标签(它比 CardLayout 更容易,因为它自动处理显示/隐藏)

Problem : My component does not appear after I have added it to the container.问题:将组件添加到容器后,我的组件没有出现。

You need to invoke revalidate and repaint after adding a component before it will show up in your container.您需要在添加组件调用revalidaterepaint 然后它才会显示在您的容器中。

Source: http://docs.oracle.com/javase/tutorial/uiswing/layout/problems.html来源: http : //docs.oracle.com/javase/tutorial/uiswing/layout/problems.html

The other individuals answered the question.其他人回答了这个问题。 I want to suggest you use a JTabbedPane instead of replacing content.我想建议您使用 JTabbedPane 而不是替换内容。 As a general rule, it is bad to have visual elements of your application disappear or be replaced by other content.作为一般规则,应用程序的视觉元素消失或被其他内容取代是不好的。 Certainly there are exceptions to every rule, and only you and your user community can decide the best approach.当然,每条规则都有例外,只有您和您的用户社区才能决定最佳方法。

The layout.replace() answer only exists/works on the GroupLayout Manager. layout.replace() 答案仅存在/适用于 GroupLayout Manager。

Other LayoutManagers (CardLayout, BoxLayout etc) do NOT support this feature, but require you to first RemoveLayoutComponent( and then AddLayoutComponent( back again. :-) [Just setting the record straight]其他 LayoutManagers(CardLayout、BoxLayout 等)不支持此功能,但要求您首先 RemoveLayoutComponent( 然后是 AddLayoutComponent( 再次返回。:-) [直接设置记录]

I was having exactly the same problem!!我遇到了完全相同的问题!! Increadible!!不可思议!! The solution I found was:我找到的解决方案是:

  1. Adding all the components (JPanels) to the container;将所有组件(JPanels)添加到容器中;
  2. Using the setVisible(false) method to all of them;对所有这些都使用 setVisible(false) 方法;
  3. On user action, setting setVisible(true) to the panel I wanted to show.在用户操作上,将 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);

No revalidate(), no validate(), no CardLayout needed.不需要 revalidate(),不需要 validate(),不需要 CardLayout。

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

Just call the method pack() after setting the ContentPane , ( java 1.7 , maybe older) like this:只需在设置ContentPane后调用方法pack() ,( java 1.7 ,可能更旧),如下所示:

JFrame frame = new JFrame();  
JPanel panel1 = new JPanel(); 
JPanel panel2 = new JPanel(); 
....
frame.setContentPane(panel1);
frame.pack();
...

frame.setContentPane(panel2);
frame.pack();
...

I suggest you to add both panel at frame creation, then change the visible panel by calling setVisible(true/false) on both.我建议您在创建框架时添加两个面板,然后通过在两者上调用 setVisible(true/false) 来更改可见面板。 When calling setVisible, the parent will be notified and asked to repaint itself.调用 setVisible 时,将通知父级并要求重新绘制自身。

class Frame1 extends javax.swing.JFrame {

    remove(previouspanel); //or getContentPane().removeAll();

    add(newpanel); //or setContentPane(newpanel);

    invalidate(); validate(); // or ((JComponent) getContentPane()).revalidate();

    repaint(); //DO NOT FORGET REPAINT

}

Sometimes you can do the work without using the revalidation and sometimes without using the repaint.My advise use both.有时您可以在不使用重新验证的情况下完成工作,有时无需使用重绘。我的建议是同时使用两者。

you can use the replace method of a layout: 您可以使用布局的替换方法:

layout.replace(existingComponent, newComponent)

the existing and new components can be JPanels, too. 现有组件和新组件也可以是JPanels。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM