I am trying use JMenuItem to remove and add the panels I need. However, when ever I use the action listener and I tell it to add a panel, nothing happens.
PanelMaker newPanel = new Panel(); //I have my panel in another class and I use this to call it
item.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
add(newPanel.pane());//I try to add the panel here, but nothing occurs
}
});
You need to revalidate and repaint the container that gets components added or removed. i.e.,
#Override
public void actionPerformed(ActionEvent e) {
add(newPanel.pane());//I try to add the panel here, but nothing occurs
revalidate(); // tells the layout managers to re-layout components
repaint(); // requests that the repaint manager repaint the container
}
The call to revalidate() tells the container's layout manager to re-layout all components held by it, and likewise can cause a cascade of re-layouts of all contained containers.
The call to repaint() again suggests to the repaint manager to repaint the container and all of its children. This is important especially if components are removed or if components move on top of a location where another component was previously seen, in order to clean up old renderings.
Also very important is the layout manager used by the container. Some don't readily accept new components easily -- GroupLayout comes to mind immediately in this regard.
Related
This is probably a very basic question, but I could't find anything about it online.
I'm working in Java swing and have a JPanel with a null Layout Manager (ie using absolute positioning). The JPanel is filling a space in the JFrame so that its size will change when the JFrame is resized. Within this JPanel, I have a number of other components that I have placed using Component.setBounds(). I would like one of these components to be set relative to the bottom of the JPanel, so that when the containing JPanel resizes, the smaller JComponent stays stuck to the bottom of the container.
I have tried to do this by overriding the getLocation() or getBounds() methods to reference the container height, but neither of these seemed to work the way overriding getPreferredSize() would, even after calling revalidate() and repaint(). Unfortunately, using another layout manager like BorderLayout is not an option here.
Is there a way to do something like this? Am I missing something obvious? If not, is there a way to listen for changes in the container's height and re-call .setBounds()?
Try listening to the resize event of the panel:
panel.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent arg0) {
component.setBounds(...);
}
});
You can reference panel.getBounds from withing this method and set your components bounds accordingly.
To do this you can add a component listener to your JFrame.
addComponentListener(new ComponentAdapter(){
public void componentResized(ComponentEvent e){
//Do stuff here
}
});
Inside the component listener you can change the sizes and locations of anything you would like to. To stick them to the bottom simply get the size of the JFrame and subtract a specific amount and set that as the y location for what you want stuck to the bottom.
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.
The problem is then I switch between panes, my buttons and text-fields become not visible, but then I drag cursor over components, they appear.
My Hierarchy looks similar to this:
JFrame
PanelWithComponents
_Button3
PanelWithPanels
_PanelOne
__Button1
__Label1(Background)
_PanelTwo
__Label2(Background)
__Button2
private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {
if (jRadioButton1.isSelected()){
CardLayout cl = (CardLayout)(bottomPanel.getLayout());
cl.next(bottomPanel);
}else{
CardLayout cl = (CardLayout)(bottomPanel.getLayout());
cl.next(bottomPanel);
}
}
You should call the revalidate() and repaint() method when you switch the panes.
frame.getContentPane().revalidate();
frame.getContentPane().repaint();
From add method of Container.
Note: If a component has been added to a container that has been displayed, validate must be called on that container to display the new component. If multiple components are being added, you can improve efficiency by calling validate only once, after all the components have been added.
From remove method of Container.
Note: If a component has been removed from a container that had been displayed, validate() must be called on that container to reflect changes. If multiple components are being removed, you can improve efficiency by calling validate() only once, after all the components have been removed.
Edited as per camickr's comment.
got some problems.
Built an applet that has to be used step-by-step. After each step, a button is clicked and the next step should be added into the GUI.
Problem: without zooming, the added content doesn't get visible. in a application you can workaround with scaling the window size, but in an applet I wasn't able to solve that problem.
Thanks
EDIT:
actually, it looks smth like this:
Panel cp = new Panel(new GridLayout(0,2));
Panel Block1 = new Panel(new GridLayout(2,2));
Panel Block1 = new Panel(new GridLayout(2,2));
...
init
public void init()
{
buildBlock1();
buildBlock2();
add(cp);
cp.setVisible(true);
}
adding some empty panels here, those who will get filled afterwards
private void buildBlock1()
{
Block1.add(panel1);
Block1.add(panel2);
Block1.add(panel3);
Block1.add(panel4);
cp.add(Block1);
}
button actionlistener
private void generatePanel1()
{
//adding some Components to the subpanel of Block1, which is a subpanel of cp.
Panel1.add(...);
cp.repaint();
cp.validate();
}
Now I don't have any code that I can check but adding (and removing) components to a container normally often needs to be "validated". So try
panel.add(...);
panel.revalidate();
API docs for JComponent.revalidate():
Validates this container and all of its subcomponents.
Supports deferred automatic layout.
Calls invalidate and then adds this component's validateRoot to a list of components that need to be validated. Validation will occur after all currently pending events have been dispatched. In other words after this method is called, the first validateRoot (if any) found when walking up the containment hierarchy of this component will be validated. By default, JRootPane, JScrollPane, and JTextField return true from isValidateRoot.
This method will automatically be called on this component when a property value changes such that size, location, or internal layout of this component has been affected. This automatic updating differs from the AWT because programs generally no longer need to invoke validate to get the contents of the GUI to update. The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed.
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).