Java: Changing Panels during runtime - java

The following method has the end result I need. The problem is, when the method is called, the starting pnlMain stays visible until the new pnlMain is created and replaces the original.
The point of this method is to change the panel by creating a new one but this process takes a little time, so I am trying to have the "load" panel show up during that time.
public void changePanel() {
remove(pnlMain);
add(load);
repaint();
pnlMain = new HunterPanel(settings); // HunterPanel extends JPanel
remove(load);
add(pnlMain);
repaint();
pnlMain.requestFocus();
}

"Changing Panels during runtime"
The correct way is to use a CardLayout that will allow you to switch between views. See How to Use CardLayout for more details. See a simple example here
But just to let you know where you're going wrong in your code, if you remove and add components at runtime, you need to revalidate(). But in your case, go with the CardLayout. After you learn it, you be happy you did :-)

Related

How to change JPanel using KeyListener? [duplicate]

I am developing a game, where you first get to the main screen, there are multiple selections to go, for example, Singleplayer, Twoplayer, Credits, etc.
I have one big problem. If I click a button in the menu, (not JButton) the JPanels switch, but the keyListener is lost. The Keylistener is in the same class as the game code, which implements JPanel. I tried everything to get the Keylistener to work, but it just won't.
Here is how the things are called: Main class --> Menu --> Game. I tried adding the keylistener to the main class, but it's not working.
So, JPanel switching is ok, but the Keylisteners are gone. I was developing the game before with new JFrames, so when I clicked a menu, a new frame was created. I didn't insert a code here, because it's too long (2000+ lines), and the KeyListener is working, but only when it is in a new JFrame. I set the mode int in the Menu class, by clicking a button.
This is currently my panel switch:
public void setJPanel() {
switch (mode) {
case 1:
getContentPane().add(s);
validate();
break;
case 2:
getContentPane().removeAll();
getContentPane().add(sp);
validate();
break;
}
}
Thanks for your help in advance!
Rather than use a KeyListener, have you given thought to or tried using Key Bindings? KeyListeners require that the component being listened to has focus, and focus may be lost for many reasons, especially when swapping views (are you using a CardLayout for this?). Key Bindings on the other hand can be set to be responsive even if the bound component doesn't have focus but when it is only held within a window that has focus. Tutorial: Using a CardLayout
Edit
I see that you're not using a CardLayout, and I suggest that you use this as it can make your view swapping cleaner and easier.
Edit 2
I agree that you don't want to post your entire 2000+ line program here as no one will have the time to read it, but consider condensing your question/problem into a single small class that is compilable and runnable by any and all of us, and demonstrates your problem. In other words, a Short, Self Contained, Compilable, Example or SSCCE .
Remember, the code should be compilable and runnable for many of us to be able to understand it fully.
Cardlayout actually is screwy while refocusing.
#op, try calling requestFocusInWindow() after the new jpanel was added
Try using myPanel.requsetFocusInWindow();
before using setVisible(true);

Copying JPanel contents to another JPanel without removing contents of original JPanel

I am new to Java swing coding. I am trying to copy JPanel contents to a new JPanel which uses contents of original JPanel to show. Also, the original JPanel contents are changing as records are changing. I tried the following code but it's totally useless.
public void addPanel(JPanel jp)
{
JPanel jp1=new JPanel();
int count=jp.getComponentCount()-1;
for(;i>=0;i--)
{
jp1.addComponent(jp.getComponent(i);
}
//after this I am setting bounds of jp1.
this.add(jp1);
}
This doesn't work if I want to make multiple JPanels as original JPanel changes. It overwrites the contents of new 'jp' over 'jp1' if used multiple times, say if used in a for loop.
I do not want to remove components of original JPanel. How can I do that?
Moving instances of Component is possible but coping them requires you to do it manually.
You can do this as a program by creating new instances of the origin class and then calling the setters with the values of the getters... But frankly, thats an error prone way and you'll need reflection for it which you shouldn't use unless really necessary.
What you can do is to override the standard Java Classes you use (e.G. JLabel) and in your overridden class you implement Cloneable where you set the parameters you need (text, bounds, whatever) then call your function like this:
public void addPanel(JPanel jp)
{
JPanel jp1=new JPanel();
int i=jp.getComponentCount()-1;
for(;i>=0;i--)
{
jp1.addComponent(
((Component) // this casts the clone back to component. This is maybe superfluous.
((Cloneable)jp.getComponent(i) // You have to ensure that all components that are returned are in fact instances of Cloneable.
).clone()
));
}
//after this I am setting bounds of jp1.
this.add(jp1);
}
If you go down that road, be sure to read the Documentation of Cloneable.
Here is method for solving the following problem without coding just using design views of JPanels/JFrames.
You can go to Navigator in design view select all JPanel components and copy them by typing ctrl+a and ctrl+c.
Then create another one JPanel and in design view, just paste them with ctrl+v.
Result: You get all components same size, dimensions and positions with same properties and values. After you do this, you can easily change whatever you want by using properties of GUI forms.

how can I use a variable in multiple jpanels?

I have a frame with a var , I added that var to a JPanel, and if I want to add the same var to another JPanel, it`s disappearing from the first JPanel. I want a logical explanation for my problem please, thank you !
I want to store my JLabel in both of my jpanels.
public class Gui {
JPanel panel1, panel2;
JLabel text = new JLabel("some text");
JFrame frame = new JFrame();
public Gui {
panel1 = new JPanel();
panel1.setLayout(null);
panel1.add(text);
panel1.getComponent(0).setBounds(50,50,50,50);
panel1.setBorder(BorderFactory.createLineBorder(Color.black));
panel1.setBounds(x,y,w,h);
// it`s working, the labels it`s visible
panel2 = new JPanel();
panel2.setLayout(null);
panel2.add(text);
panel2.getComponent(0).setBounds(100,100,50,50);
panel2.setBorder(BorderFactory.createLineBorder(Color.black));
panel2.setBounds(x,y,w,h);
//it`s not working, the label ins`t visible ...
frame.add(panel1);
frame.add(panel2);
}
}
Disclaimer: I am editing my answer in response to comments from the OP. However, I am still not entirely sure about some of the details of the question. I will gladly edit my answer as more clarifications are given.
Answer: One possible solution is to create subclasses of the Swing components you are using. For example,
public class MyPanel extends JPanel {
private JLabel label = new JLabel("some text");
public MyPanel() {
this.add(label);
}
}
Now you can create multiple instances of MyPanel which each have its own JLabel. Then you can add these panels to your frame.
Added: With the additional information given in the comments, I would go a step further and create a custom JFrame class:
public class MyFrame extends JFrame {
private MyPanel panel = new MyPanel();
public MyFrame() {
this.add(panel);
}
}
Now you can create several instances of MyFrame. If you want to go even further, you can add parameters to the constructors of both of these custom classes to set the JLabel text to different values in each instance MyFrame. I will leave the details as an exercise to the reader. (Of course, please ask if you get stuck, though.)
Just as it's discussed with in other posts. All Swing UI components (ie JLabel included) can only have one parent (ie JPanel). If you add it to another panel it will remove itself from the prior parent. There are very good reasons for this behavior. For example, JPanel 1 might not use the same Layout as JPanel 2 and hence the label would have to support two different placements within each JPanel. The whole point of using Objects as components is to provide encapsulation of data like (font, position within the parent, width, height, etc) inside that object. If you want two visual elements just create another element. Now that creates a problem "How do you synchronize the data across all of these controls?" That's basically scratching how do you build a proper Swing architecture for your program?
What you don't want to do is use the Swing component model (ie. Jabel, JTextField, etc) as your data model. And after reading your question and code I believe that's what you are doing. You may not realize it. UI Components should be used for display only. They reflect what is in the data model. So you'll want to create a model that doesn't involve Swing. It should model your problem regardless of how its displayed. That means you shouldn't assume it will always be Swing or web, etc.
There are very practical reasons for this. Say in a year you want to redesign your UI. If you combined the view components and data model together you pretty much have to start completely over. Even if you aren't switching technologies. Say you are switching from a JList to a JTable or a JTreeTable. Just simple changes of the types of components you have on the screen can be an absolute nightmare if you don't segment your view from the model. Plus the View displays thing, but the model might need information that isn't displayed to the user but is tied to what is being displayed. For example, the ID of the row in the database. If you stuff the view and data model together you have to play little hacks to store this invisible information in weird ways. Things that make it hard for other people to understand.
If you don't know what I mean you either will in 6 months when you have to make your first major redesign or you'll save yourself some pain now and try and understand what I mean now. Just simple POJOs will suffice. Then share those POJOs with your Swing components. For example:
MySpecialPanel panel1 = new MySpecialPanel();
MyOtherSPecialPanel panel2 = new MyOtherSpecialPanel();
frame.add( panel1 );
frame.add( panel2 );
...a little while later...
MySpecialPOJO specialPOJO = ...some call to a service...;
panel1.setSpecialPOJO( specialPOJO );
panel2.setSpecialPOJO( specialPOJO );
Notice that I created two subclasses of JPanel called MySpecialPanel and MyOtherSpecialPanel. Inside there they create the components contained within them. Then then expose a setter method taking a data model object of type MySpecialPOJO. Inside those methods we might see something like the following:
public void setSpecialPOJO( MySpecialPOJO specialPOJO ) {
this.model = specialPOJO;
textField1.setText( specialPOJO.getName() );
textField2.setText( specialPOJO.getDescription() );
radioButtonGroup.setSelected( specialPOJO.getGender() );
....
}
Now you have a way to take a model object, and update the relative UI components that make up that view. Notice it doesn't care about any other external dependencies (ie where it got the object from). It just updates the controls it owns to reflect what's carried by the POJO. Now if you follow these simple rules it makes instantiating your special panels easy. Whether you need only one instance or many instances. Also if you need to move panels within your program it's pretty easy to do that if you reduce your dependencies to just your POJOs. There's a lot more to this, but for now this will get you started. You still have to figure out how to get data out of the UI and back into your POJOs (events!). Controllers, Services, etc.
This might help you as well:
Up-to-date Swing MVC example + Question
You can't. As you noticed, a control can only be attached to one window at a time, and if you try to attach it to another one, it will remove itself from the first.
Suggestions:
panel2.add(text.clone()); // clone the existing label
panel2.add(new JLabel("some text")); // make a new label from scratch

How to empty this panel and add new panels

I have one main JPanel container and three JPanels inside. How to empty this panel and add new panels? I tried with remove(Component) but it doesn't work. Can anybody give me advice?
This will do it. The trick is to call revalidate.
mainPanel = ...
mainPanel.removeAll();
mainPanel.add(newPanel1);
mainPanel.add(newPanel2);
mainPanel.add(newPanel3);
mainPanel.revalidate();
But really, consider using CardLayout, if you want to change what appears in a JPanel.
Here in this link i found a simple tutorial on how to add and remove elements from panels.
The other panels inside your main panel, are also elements, so the same principle applies to them.
A good practices when adding something new in the panel is not just to use the method add():
we might also want to use revalidate() and repaint() They should be called when some event occurs(button clicked or similar...)
Also i want to mention that in the tutorial remove() i being used to remove elements, you are doing it corretly. Maybe calling again revalidate() and repaint() for the other panels make the removed panel dissapear from the GUI(The object is deleted just the GUI is not refreshed)
Note: I suppose that the elements of your inner panels are visible = true. If some of the inner elements struggle to render try to call also revalidate() and repaint() at them.
I think this way should work.
# Harry Joy
if you added or removed (already visible container) then you have to call
revalidate();
repaint(); // not required in all cases
# Damir
if JComponents isn't public (or private) static then you can just call
myContainer.removeAll();
myContainer.revalidate();
nyCOntainer.repaint();
possible is remove JComponent(s) by some parameter(s) with Component[] a = myContainer.getComponents(); then you could call if (components[i] instanceof JComboBox) { ...
Try the other remove method remove(int index);
this works 100%
this.panelname.Controls.Clear();
I too had the same problem. All I did to resolve the issue was
panelName.setVisible(false);
mainPanel.remove(panelName);
In my case, panelName is a JPanel which lies inside mainPanel.

java validate() method doesnt work properly

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

Categories