Update child (panel) of CardLayout - java

My main JFrame content panel which is card layout and I have added many (say panel1, panel2) panel as card. I would like to update UI panel1 when something changes in panel2.
How to update child (panel) of CardLayout in Swing?

The observer pattern, discussed here, is the key to this. In particular, both panels could listen to a common model, which would fire events to update each listening panel. Examples using PropertyChangeListener may be found here and here.

Maintain a reference to the target of what you want to change, and use some kind of callback function to detect the changes. Use the stored reference in the callback function. How you specifically implement this should be whatever makes most sense for your code... but what you are trying to do is a simple task.

Related

Swing: perform action on main JFrame

Say I have various JFrames open in the same application. Is there a way to perform some action (like update a JTable) when the user changes the focus on one frame to another (like clicking the bar on the top)?
If not is there a way to perform an action on one JFrame when she closes another JFrame?
Please read: The Use of Multiple JFrames, Good/Bad Practice? to see why your program design could very well be improved
As for your main question,
Is there a way to perform some action (like update a JTable) when the user changes the focus on one frame to another (like clicking the bar on the top)?
It's really a specific example of a more general question:
How do I change the state of one object through an event that occurs in another object
and there are several possible solutions
Easiest would be to have the code that handles the event have a reference to the first object, here one of your JFrames, and simply call a method on it when the event is triggered.
Better is to structure your program with an MVC (Model-View-Controller) type structure, and in the event code (the control code), change the state of the model. View listeners to the model, including the JFrame you wish to change, would then be notified and would change their appearance based on the model.
Some general recommendations:
Having a bunch of windows displayed and swapped is very annoying to the user. Have a look at my link above to look for other possible options.
You'll probably want to avoid having class's extend JFrame, as that forces you to create JFrames with that code. Much better is coding to the JPanel, not the JFrame, and then placing the JPanels created wherever they are needed, be it within a JFrame, or in another JPanel, or swapped via a CardLayout, or in a JTabbedPane, a JDialog, a JOptionPane...

JFrames inside JFrames

I currently have build an application where I use multiple frames.
But it would be nice if I could use the frames I used all in just 1 frame.
Like in the image below.
So if you press the left button "Speler Overzicht" that it will show the users in the right panel and I still have my buttons in the left panel.
Generally speaking, it's a very bad idea to base you UI classes on JFrame, as it locks you into a single use case, meaning you can't add the UI component (frame) to other containers.
I better solution is to base your UI components on JPanels, which then allows you to add them to where ever you need them. It also makes life easier to extend them, but that's another story.
To allow the user to move between multiple views, you can use either a CardLayout or JTabbedPane depending on your needs
See How to Use CardLayout and How to Use Tabbed Panes for more details
Use JPanels instead.
buttonPanel=new JPanel();
overzichtPanel=new JPanel();
buttonPanel.add(button);// do this for every button
overzichtPanel.add(componentsYouWantToAdd);// replace with your variables of course
frame.add(buttonPanel, BorderLayout.WEST)
frame.add(overzichtPanel, BorderLayout.CENTER)
You cannot put one JFrame inside another. You have a some design choices here. You can change your JFrames to JPanels. This is probably the easiest change. On the other hand, you can look at using Internal Frames instead.

Dealing with Swing Actions in several UI JPanels without a reference to the JPanels themselves

My first problem is: "how do I limit the amount of Swing Actions (using the AbstractAction as a base for other actions) when the same actions are used in conjunction with different UI components that are referenced with different Swing JPanel objects that (seem to have) no direct reference to eachother"?
My second problem is: "how to keep referenced between Actions and UI components when the actionPerformerd() method of the action uses TWO or more different UI component references"? I need ui context within the actionPerformed() method at different places that have no direct relation (well, at least that is what I think).
Let me explain my design concerns I am dealing with...
I have one JFrame which holds all UI logic. This JFrame holds three different references to JPanel logic: a ControlPanel, a MainPanel and a StatusPanel (all extend JPanel).
The ControlPanel sets up the JMenu and items, the JToolBar items and this JPanel is added to the contentpane with all the approriate Actions installed.
The MainPanel is where the actual communication with the user takes place. This JPanel holds a CardLayout with a bunch of JPanels inside it.
The StatusPanel is actually a statusbar dealing with all kinds of text messages coming from differnt JPanels, but processed by a StatusBarManager object. No fancy stuff here, just boring gui stuff.
Now the problem part: I first programmed a bunch of extended AbstractActions for each action I could think of (not so good idea). Next I programmed an ActionFactory that hands out an Action obj for a particulair task, like: ActionFactory.getAction("file.new");
This seems like a better approach because I now can instantiate a FileAction object for all "file" operations with one difference: the implementation of the actionPerformed() method. This "seems" a good idea because when I call in a different JPanel the same ActionFactory.getAction("file.new"); I get the same Action object returned that already was instantiated before (saves a lot of duplicate objects).
Well this might seem a good idea, I still struggle with the context of the JPanels. When I select a "card" to display within the MainPanel using the JMenu and JMenuItems, I am in the ControlPanel ui context only and in the actionPerformed() method I do not have the MainPanel context to set the "card" using the CardLayout of that JPanel.
The same goes for when I am in the MainPanel (pressing a button) and have no context of a StatusPanel.
I have looked at these previous suggestions on stackoveflow, but somehow I can't make the connection. I am missing the clue...
Sort of same Q&A over Actions at SO:
here, here, here, here, here, here, here, here, and here .
Some suggested to use a framework for this like the appframework or eclipse rcp or the spring rcp, or others. BUT I prefer to not use any frameworks at all for several reasons other than what is available in the default JVM 6.
I hope someone can shine his/her light over this matter that helps me find a good solution for my problem. Thanks for your help in advance!
I think the main flaw in the design you describe is that the actions work directly against the UI, instead of against a model/controller.
If your action just updates a model, and each view part keeps itself in sync with the relevant part of the model, you avoid those dependencies between one action and several other view components.
This also allows to share the common logic. You move that logic from the action to the model/controller, and the action becomes basically a one-liner (calling that logic).

Refresh Panel on CardLayout Swap

How do you refresh a JPanel on the panel change from container CardLayout?
Use the show() method. From the Java API:
Flips to the component that was added to this layout with the specified name, using addLayoutComponent. If no such component exists, then nothing happens.
CardLayout#first(), next(), and previous() do something similar.
Sometimes, when I've made a panel swap like this (though not that I can remember on CardLayout, but it's been a while since I used it), I've also needed to pack the frame again. If that doesn't work, you can call revalidate(), which lets Swing know that the component needs to be redrawn.
You may also want to consider using a tabbed pane, as it does a lot of this for you; I started out a project trying to use CardLayout and decided to use a the tabbed pane instead. Depends on what you want to do, of course.
Is there an actionlistener or something that I can have it reload/refresh my data on that screen?
Assuming you have a model that supplies data to your view (panel), two approaches are common:
Arrange for your model to extend Observable and your view to register as an Observer.
Arrange for your model to manage an EventListenerList, in effect creating you own analog of ActionEvent.
In either approach, use the listener of the control that switches views tell the model to update its registered observers/listeners. The example in How to Use CardLayout uses a JComboBox and itemStateChanged(). There's additional discussion here.
Is there an actionlistener or
something that I can have it
reload/refresh my data on that screen?
You can use an AncestorListener. It fires when a component is added to a Container. So when you swap cards the event is fired. You would need to add the listener to each panel you add to the CardLayout:
Another problem with the CardLayout is that the panel doesn't gain focus when it is swapped. I use this approach to set focus on the panel. Check out Card Layout Focus.
panel.addAncestorListener(...);
The show() method does the trick of switching panels, but it doesn't "refresh" the data. Is there an actionlistener or something that I can have it reload/refresh my data on that screen?

Best way of subclassing a JPanel in Swing

I am currently trying to build an expanding panel in Swing (akin the WPF's Expander control) and I'd like to retain the usual methods for manipulating it (i. e. setLayout, add, etc.). Only they should be routed to an embedded panel (the one being shown or hidden).
How would one do that? Overriding every method of JComponent and re-routing that to an embedded JPanel would be cumbersome, but that's the only way I see.
Or should I rather make the embedded panel visible to the outside and force users to use something like ExpanderPanel.getInnerPanel() instead. But then it's no drop-in replacement for JPanel which I think would be nice to have.
Take a look at the JXTaskPane from Swingx project. It already does what you need.
In 1.5(ish) Swing routed a few methods to the content pane in JFrame, JApplet, etc. Whilst there appeared to be some usability benefits for those just starting, it doesn't actually fix the problem. So everyone has to deal with a very strangely behaving API. So my advice is to avoid this approach.
If you have a Container widget which holds a panel you want to show and hide, why not layout your inner panel however you want, then add it to the Container panel, then use static methods against the Container to say
JPanel p = new JPanel();
//do something with the JPanel...
ContainerWidget.setContent(p);
ContainerWidget.expandPanel(p,true);
Would somethign like this work?

Categories