I have a class that extends javax.swing.JPanel, it contains a single JButton. I've created the class in NetBeans designer. As such, I have a initComponents() function thats called from the class constructor.
What I want to do is when/if a function is called, I want to add a second button, and change the layout of the two buttons. Doing simply:
public void addSecond() {
javax.swing.JButton secondButton = new javax.swing.JButton();
add(secondButton , java.awt.BorderLayout.CENTER);
}
Doesnt work, the new button doesnt show up. I tried a call to invalidate() as well but no luck.
How do I trigger a re-evaluation of the layout?
If said function is called more than once, what parts of the layout/buttons do I need to call dispose() on? Any other cleanup I should worry about?
Would this be easier to handle if I don't use the NetBeans designer?
You need to set the layout of the panel before you add the button with BorderLayout.CENTER. Also, you must remove and add the first button again and invoke the revalidate() method on the panel.
Change your addSecond() method as below and it should work.
private void addSecond() {
JButton secondButton = new JButton("Button - 2");
this.setLayout(new BorderLayout());
remove(firstButton);
add(firstButton, BorderLayout.NORTH);
add(secondButton, BorderLayout.CENTER);
revalidate();
}
when you changed the components in a way that changes the layout, you need to trigger the layout manager again by calling revalidate(). You can call it as often as you want.
For simple layouts just calling repaint() may be sufficient.
And actually unless you're doing dynamically changing panels (i.e. adding/removing components on the fly) you should use the netbeans designer, so all the Swing elements are in one place.
-- EDIT --
And you can only put one component into BorderLayout.CENTER per panel. If you put more than one element into the same position of a panel, what gets painted is not well defined, i.e. it may be either of the elements (or both).
Related
To put it simple, there's a simple java swing app that consists of JFrame with some components in it. One of the components is a JPanel that is meant to be replaced by another JPanel on user action.
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.
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:
JFrame frame=new JFrame();
frame.getContentPane().add(new JPanel());
2)Replacing the panel:
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
frame.setContentPane(newContents());
frame.revalidate(); // frame.pack() if you want to resize.
Remember, Java use 'copy reference by value' argument passing. 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. 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.
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.
Game game = new Game();
getContentPane().removeAll();
setContentPane(game);
getContentPane().revalidate(); //IMPORTANT
getContentPane().repaint(); //IMPORTANT
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. If you are only switching from the first to the second once and (and not going back) then I would use telcontars suggestion and just replace it. Though if the JPanel isn't the only thing in your frame I would use
remove(java.awt.Component) instead of 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. 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)
The other individuals answered the question. I want to suggest you use a JTabbedPane instead of replacing content. 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.
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.
Source: http://docs.oracle.com/javase/tutorial/uiswing/layout/problems.html
I was having exactly the same problem!! Increadible!! The solution I found was:
Adding all the components (JPanels) to the container;
Using the setVisible(false) method to all of them;
On user action, setting setVisible(true) to the panel I wanted to
show.
// 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.
The layout.replace() answer only exists/works on the 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]
I suggest you to add both panel at frame creation, then change the visible panel by calling setVisible(true/false) on both.
When calling setVisible, the parent will be notified and asked to repaint itself.
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.
Just call the method pack() after setting the ContentPane, (java 1.7, maybe older) like this:
JFrame frame = new JFrame();
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
....
frame.setContentPane(panel1);
frame.pack();
...
frame.setContentPane(panel2);
frame.pack();
...
Okay currently I am trying to make a multi-window program.
And from seeing other forums, it seems for you to do that in Java JFrame you must update its content pane by adding the new JComponent(new window/layout/idk), set the current window visibility to false, set the new one visibility to true and validate &/ repaint content pane:
contentPane.add(newWindowPanel);
currentWindowPanel.setVisible(false);
newWindowPanel.setVisible(true);
contentPane.validate();
contentPane.repaint();
now, what I am trying to do and partially have done is created a class that extends JPanel, and this class stands to be the top of an hierarchy for many other JPanel classes that I am going to create.
Within that class I have this method :
public void updateContentPane(Container contentPane, JPanel currentPanel, JPanel nextPanel){
contentPane.add(nextPanel);
currentPanel.setVisible(false);
nextPanel.setVisible(true);
contentPane.validate();
contentPane.repaint();
}
When I call this method within one of the child classes, it doesn't work.
updateContentPane(WindowMain.contentPane, this, mainMenuClass);
Each of the child class inherits the JPanel characteristic.
"WindowMain" is a class that extends JFrame, and "contentPane" is a static container variable that holds the frame contentPane.
"this" represents the current class (inherits JPane), but "this" don't actually work new Object() works.
"mainMenuClass" also inherits JPanel and has already been instantiated in this class.
My goal is to simply jump from one scene to the other by calling that method. But it goes through the code (debug) but nothing happens. But, if I take the code within the method and place it inside a button listener it works fine.
(Sorry for all this writing, it will probably bring some confusion at that, but I need to figure this out nonetheless, and I will set condition for when the contentPane already contains a class, so no need to mention it)
You could just update the whole frame by doing
frame.revalidate();
frame.repaint();
Coded this in netbeans so I did not write all the code for the creation of the rest of the GUI myself.
btn_Next is the button that is already on the panel
private void place_Button() {
btn_Next.setLocation((btn_Next.getX()+30), btn_Next.getY());
btn_Next.revalidate();
btn_Next.repaint();
JButton btn_Back = new JButton("Back");
pnl_Buttons.add(btn_Back);
btn_Back.setPreferredSize(btn_Next.getPreferredSize());
btn_Back.setLocation((btn_Next.getX()- 100), btn_Next.getY());
btn_Back.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
switch_Works_Back();
}
});
btn_Back.setVisible(true);
pnl_Buttons.revalidate();
pnl_Buttons.repaint();
}
What layout manager does pnl_Buttons use? If you don't know, you could easily have Java print it out:
System.out.println(pnl_Buttons.getLayout());
Note that some layout managers allow adding components much better than others, and for your question, the layout is key.
A guess here, but it looks like your pnl_Buttons uses a null layout, and if so, your JButton may not be showing because its size is 0 x 0 since you never set its size; this is because null layouts require that the added components specify completely their own size and location. You specify the location and the preferred size of the button but not its size. If so, a quick solution is to set the JButton's size via setSize(...), but much better is to not use null layouts, and instead use one of the more user friendly layout managers.
As an aside, you shouldn't call revalidate() and repaint() on the component being added and don't need to call setVisible(true) on your JBUtton unless you've called setVisible(false) on it previously. Instead you only need to call revalidate() and repaint() on the container that you're adding your component to, here your pnl_Buttons object.
I have a JFrame inside of which is a jpanel that im using as the content pane.
So basically im using the jpanel to load content into on click. New content is returned as a Jpanel also so its ends up being jpanel -> inside jpanel -> inside Jframe. When i need to load in new content i clear the panel, load the new content and validate() the jframe & jpanel and the new content displays.
My problem is that when the new content displays its clear that the validate method is working because i can see the new interface but i can also see the old interface as if its become the background; i can resize the window and it just disappears and looks as it should.
Is this just the way validate works or can i fix it?
Edit: this worked. The problem was i wasn't calling repaint manually.
public BaseWindow setContent(JComponent comp){
contentPane.add(comp);
contentPane.revalidate();
contentPane.repaint();
return this;
}
Generally the code for adding/removing one or two components from a panel is:
panel.remove(..);
panel.add(...);
panel.revalidate();
panel.repaint(); // sometimes needed
However, if you are replacing all the components on the panel, then the better approach is to use a Card Layout.
You have already stated the revaliate() followed by repaint() doesn't work so the only suggestion I have is to post your SSCCE that demonstrates the problem.
Don't use validate. Use revalidate() instead.
Revalidate first calls invalidate() followed by a validate().
In Swing, you would rarely use validate().
Note: I also feel that maybe the old panel is not cleared/removed.Check again!
Validate() is for causing components to re arrange themselves according to the layoutmanager that you have installed. This is not really what you should be using.
I can't see your code, so I'm not sure exactly what you are doing. I could speculate that calling repaint() on your "inner panel" will solve the problem you are having...but really, if you are doing things properly, you shouldn't need to call repaint() or validate().
Make two JPanels, one with content A (e.g. your buttons), and one with content B (e.g. your "static" field). Use the "add()" and "remove()" methods on the parent container (the JFrame's content pane?) to swap these two JPanels with each other whenever you want to switch the content that is displayed in that part of the JFrame.
Then you shouldn't need to do anything else; it should just work.
I don't know if validate() makes any promise about fully repainting the container. You might have to call repaint() yourself to make it behave as you want to.
Here's another possible solution:
Put both JPanels in at the same time, side by side, and then make sure only one of them is ever visible at any one time:
JPanel p = new JPanel(new BorderLayout());
p.add( panelA, BorderLayout.EAST );
p.add( panelB, BroderLayout.WEST );
panelA.setVisible(true);
panelB.setVisible(false);
Then when the user clicks the button to switch panels:
panelA.setVisible(false);
panelB.setVisible(true);
The setVisible() method and BorderLayout should take care of validating, layout, and calls to repaint() for you.
I ended up fixing my issue (display not shown, buttons would stay clicked/weren't unclicking) by changing which panels were added/removed.
Problem:
frame.removeAll();
frame.add(getNewPanelDisplay());
frame.revalidate();
frame.repaint();
Solution:
//initializer()
mainPanel = new JPanel();
frame.add(mainPanel());
// ...
//update()
mainPanel.remove(0);
mainPanel.add(getTablePanel(), 0);
frame.revalidate();
frame.repaint();
I have several JPanels that contain buttons, labels, etc. that I want to switch between from a main JFrame. Currently I am trying to use the this.add(JPanelname); method and this.remove(JPanelname); with the validate(); and repaint(); methods
The problem is it will add the panel to the JFrame but it will not remove it. I am not sure how exactly to go about this.
Maybe you should be using a Card Layout.
Or maybe you should be using modal JDialogs. So whenever you click on the "widjet" a new window is displayed. Then when you close the dialog you are back on your main frame.
If you are constantly switching between JPanels, then a JTabbedPane may be the right thing to use. It should not be necessary to call "validate" or "repaint" when you add or remove a JPanel. Do you have a layout manager installed? Do you make sure to call add/remove only within the UI event thread? Also, typically one does not call "validate()" but rather "invalidate()" to invalidate the container for updates.