What's the difference between the repaint and SwingUtilities.updateComponentTreeUI? - java

I have three JPanel, fatherPanel, childPanel1, childrenPanel2.
When I click a button, I remove the current children panel from the father panel, and add another children in the father panel.
Everytime I should call revalidate() and repaint() to update the UI.
Then, I know the SwingUtilities.updateComponentTreeUI() has the same effect.
I want to know are there any difference between the twos?

Swing supports pluggable Look-n-Feel's. When you change the L&F at runtime you need to inform all your components, about this change using the method updateComponentTreeUI. Because due to new L&F the component sizes can be changed, Swing must call revalidate to recalculate the layout. Here is the code of the method updateComponentTreeUI
/**
* A simple minded look and feel change: ask each node in the tree
* to <code>updateUI()</code> -- that is, to initialize its UI property
* with the current look and feel.
*/
public static void updateComponentTreeUI(Component c) {
updateComponentTreeUI0(c);
c.invalidate();
c.validate();
c.repaint();
}
So yes, you can invoke SwingUtilities.updateComponentTreeUI to inform your GUI about layout change but it's huge overhead (and can theoretically have some side-effects). Combination of revalidate and repaint is better in your case.

Related

Is frame.revalidate() important to get output after an event?

So, I've wrote a code where you get a rectangle after you click the button on the south region of the frame.
But the code didn't seem to work after the button is clicked because of this
public void actionPerformed(ActionEvent ev){
Drawing d = new Drawing();
frame.add(d);
frame.repaint();
}
and the I add revalidate() inside the block.
public void actionPerformed(ActionEvent ev){
Drawing d = new Drawing();
frame.add(d);
frame.revalidate();
frame.repaint();
}
now the code works just fine, but from where I've been studying they didn't use revalidate() and their code worked.
why is this happening?
So, the example you've cited is using a custom painting approach to paint the component. This does not (directly) affect the size or location of the component, so it doesn't need the container to perform a new layout pass.
See Performing Custom Painting and Painting in Swing for more details
Your code is adding a new component to the container, so you need to tell the container when you want the container to be laid out again.
So, from the JavaDocs
public void revalidate() 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.

Does SwingUtilities.updateComponentTreeUI() method set the current L&F to all super and sub components?

Background:
From the documentation, the method SwingUtilities.updateComponentTreeUI() ask to each component in the tree (What tree?) to update their own UI properties calling the method updateUI().
SwingUtilities.updateComponentTreeUI() is strictly recommended to be called after we set the LAF, calling the method: UIManager.setLookAndFeel(LAFClassName), otherwise the LAF theme could not be applied to some components and, worst, some components could not be redesigned (and some exceptions could be thrown?).
I am new to this kind of things and I discovered the method SwingUtilities.updateComponentTreeUI() after the discover of the more suggestive UIManager.setLookAndFeel(LAFClassName) method.
Before discovering SwingUtilities.updateComponentTreeUI() I had been having some problems/Exceptions/NullPointerExceptions, and the truth is that I really wasn't understanding the reason of them until I didn't discover the SwingUtilities.updateComponentTreeUI() method, which made me understand that some components in the tree could not be updated to the new LAF.
Concrete/real problem:
For instance, if I have a
subJFrame
(instantiated dynamically or triggered by an ActionEvent (click) in the
mainJFrame
), where I set the look and feels properties, and then I call SwingUtilitiesupdateComponentTreeUI() passing to it a reference to the mainJFrame:
SwingUtilities.updateComponentTreeUI(mainJFrame),
is this last one going to update all its sub-components/subJFrames (and hence updates the subJFrame where I set the LAF) ?
This is not a trivial question (also keeping in my mind what the documentation says), because I call the UIManager.setLookAndFeel(LAFClassName) method in the subJFrame (what actually this call does?) and then I call SwingUtilities.updateComponentTreeUI() method passing to it a reference to the mainJFrame, so what LAF theme is going to be applied to all the sub-components of the mainJFrame? And if some of the sub-components/subJFrames haven't been initialized yet?
You never need to call updateComponentTreeUI if you only set the look and feel before creating components.
If you change the look and feel after having already created components then you need to call the updateUI() method on every existing component. This is what updateComponentTreeUI assists with: it calls updateUI() on the component, as well as all the components it contains (and all the components that they contain; this is what it means by "tree").
If I have a subJFrame where I set the look and feel, and then I call SwingUtilities.updateComponentTreeUI() passing to it a reference to the mainJFrame: is this going to update all its sub-components/subJFrames (and hence updates the subJFrame where I set the LAF)?
If by subJFrame you mean a window that is directly contained within the other (a JInternalFrame) then yes. If you mean a frame which was merely opened from the other frame, but is a separate top-level window, then no, it won't update it, because it is not contained within the component that is being updated.
This is the loop I use for updating all top-level windows after changing the look and feel:
for (Window w : Window.getWindows()) {
SwingUtilities.updateComponentTreeUI(w);
if (w.isDisplayable() &&
(w instanceof Frame ? !((Frame)w).isResizable() :
w instanceof Dialog ? !((Dialog)w).isResizable() :
true)) w.pack();
}
That loop won't catch any components that are not currently attached to windows. For example, this can include JFileChoosers, if you keep instances of those in variables when they're not actively being displayed, in which case you will need to call updateComponentTreeUI separately on those.

Why is componentShown() not called?

I have a CardDetailsPanel class which contains several JLabels and JTextFields. This class in contained in a AddCardsPanel and is initialized as follows:
cardDetailsPanel = new CardDetailsPanel(true);
add(cardDetailsPanel, java.awt.BorderLayout.CENTER);
I also have a JLabel that contains instructions. I want to update this label when the CardDetailsPanel first appears and when focus changes to each JTextField. I have found the addFocusListener() method that will work for the later. However, my compenentShown() method isn't working for the former:
addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentShown(java.awt.event.ComponentEvent evt) {
formComponentShown(evt);
}
});
(Okay, I know this is ugly. It was generated by NetBeans.)
private void formComponentShown(java.awt.event.ComponentEvent evt) {
this.frame = (BaseballFrame) this.getParent().getParent().getParent().getParent().getParent().getParent();
}
(Yah, this is even uglier. I'll deal with the chained getParent() calls later. I want to do other things here as well.)
So why doesn't my listener get called? And how do I write a listener that will perform some actions whenever my CardDetailsPanel appears on the screen?
Use an AncestorListener as described in dialog focus.
When a JDialog (or JFrame for that matter) is made visible, focus is placed on the first focusable component by default. There may be times when you want to change this behaviour. The obvious solution would be to invoke the requestFocusInWindow() method on the component you wish to receive focus. The problem is that this doesn’t work all the time.
...
The problem is .. a component can’t request focus unless it has been added to a “realized” dialog. A realized dialog basically means that the Swing JDialog has been added to a peer component that represents a dialog on the underlying OS. This occurs when you invoke the pack() or setVisible(true) methods on the JDialog.
And that is where the ancestor listener comes in handy. For a component in a modal dialog, it will be fired once the component becomes visible, and is realized & focusable.
Edit:
The above comment applies to components in any Swing container, including JFrame and JPanel.

Swing: Dynamically Displaying a Panel

I'm currently making a java swing GUI with the Netbeans GUI builder. I have a bunch of panels that are being replaced on my main JFrame as the user navigates the GUI and a controller class is taking care of this. At one step, however, there is a panel (FilterDefinitionPanel) that contains a combo box as well as a blank inner panel (QueryHelperPanel).
What I'd like to do is to swap out this inner panel with another I've created (StringQueryDefinitionPanel) depending on what the user selects in the combo box. So right now under my combo box's ComboBoxItemStateChanged event handler I have my controller class run this method:
public void selectFilterAttribute(Object item) {
/**
* Determine panel to create based on item selection. Currently always returns the same
* StringQueryDefinitionPanel.
*/
JPanel panel = this.getRequiredQueryHelperPanel(item);
/**
* Swap the placeholder QueryHelperPanel with the required one.
*/
((FilterDefinitionPanel) this.mainFrame.getMainPanel()).setQueryHelperPanel(panel);
/**
* Not sure if all of these are needed :\
*/
mainFrame.validate();
mainFrame.repaint();
mainFrame.pack();
}
This is what's happening in the FilterDefinitionPanel's setQueryHelper method:
public void setQueryHelperPanel(JPanel panel){
this.remove(queryHelperPanel);
this.queryHelperPanel=panel;
this.queryHelperPanel.repaint();
/**
* Again, not sure which refresh methods are needed...
*/
this.validate();
this.repaint();
}
Currently I think this is replacing my inner placeholder panel with ...something... , but the replacement seems to contain nothing. I don't know if it matters but both the placeholder and the replacement panel are the same size. I'm kind of a swing nub so any tips would be really appreciated.
The setQueryHelperPanel() method does not work because it removes the existing panel from the collection of children of this via the remove() method, but then does not use add() to add the new panel - assigning it to the instance variable does not cause it to become a child.
However, a much cleaner solution for your problem would be to use a CardLayout.
Thanks for the reply.
I looked into CardLayout previously but I'm trying to avoid it as from what I understand it loads all the panels in memory and then allows you to swap them out. I'd like to keep full control over when I dynamically display the panels.
Forgetting the remove was a dumb mistake on my part. I actually had that earlier but it still wasn't working. This is the current swap method:
public void setQueryHelperPanel(JPanel panel){
this.remove(queryHelperPanel);
this.queryHelperPanel=panel;
this.add(queryHelperPanel);
this.queryHelperPanel.repaint();
this.validate();
this.repaint();
}
I understand that setting the instance variable doesn't actually add it as a child, but my idea was that the variable would be used to remember what panel to remove or add.

Java Swing: dispose() a JFrame does not clear its controls

I have a closeWindow() method which uses dispose() for the current JFrame to close down. When I show the window again, the controls (textboxes, lists, tables etc.) still have their previous values in place that were there when I dispose():d the frame... Why is that? Is there another way to completley close and clear a frame?
This is the code that another JFrame uses to show the other window, am I doing something wrong here?
#Action
public void showAddProductToOrderView() {
if (addProductToOrderView == null) addProductToOrderView = new AddProductToOrderView(this);
addProductToOrderView.setVisible(true);
}
Disposing a window will not clear its child text components. Dispose will release native resources. The javadoc for java.awt.Window also states:
The Window and its subcomponents can be made displayable again by rebuilding the native resources with a subsequent call to pack or show. The states of the recreated Window and its subcomponents will be identical to the states of these objects at the point where the Window was disposed (not accounting for additional modifications between those actions).
As suggested by others, create a new instance each time instead. If that's to expensive I believe your best option is to clear sub components when the view becomes visible, e.g. by overriding setVisible.
EDIT:
Remove the null check to create a new frame each time.
#Action
public void showAddProductToOrderView() {
addProductToOrderView = new AddProductToOrderView(this);
addProductToOrderView.setVisible(true);
}
I don't know about the rest of your code, if there's something else depending on the frame being reused. For example, if you have attached listeners, ensure they are unregistered to not leak them.
The simplest thing to do would be to re-create the whole frame (using its constructor) before using show() to show it again. That will give you a whole new set of components, assuming that the constructor creates and places them.

Categories

Resources