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.
Related
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.
When I set the visibility of components (mainly JPanels that have other child components) to false or back to true later, do I have to call .revalidate() and .repaint() on the parent container explicitly or do the LayoutManagers handle this automatically?
When I tried this with a component in BorderLayout-North position for example, everything looked fine without calling revalidate. Just not sure if it will work on all platforms and with all LayoutManagers.
when u set the visibility of components like JPanels. JVM automatically call revalidate() and repaint() method when u set the visibility to true. u does not need to call the all these method. and it will work for all layout
The following code may solve your problem
p2_wrkrreg=new JPanel();
p2_wrkrreg.setBounds(201,0,830,720);
// p2_wrkrreg.setLayout(null);
//p2_wrkrreg.setBackground(Color.white);
p2_wrkrreg.setVisible(false);
In this code,
The JPanel declared and make it invisible, it will be visible on a button click
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btn_wrkrreg)
{
p2_wrkrreg.setVisible(true);
}
here btn_wrkrreg is a JButton
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.
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.
I have a simple object which extends JPanel, when the update() method is called on this object it it meant to add some labels to the panel and then repaint. However the labels do not show up after the update method is called, below is the code for update:
public void update(){
GridBagConstraints constraints = new GridBagConstraints();
if(cardsHidden){
for(int i = 0; i < 2; i++){
constraints.gridx = i;
constraints.gridy = 0;
JLabel card = new JLabel(PlayingCards.cardImages[PlayingCards.CARD_BACK_INDEX]);
add(card, constraints);
}
}
else{
Card[] holeCards = player.getHoleCards();
for(int i = 0; i < holeCards.length; i++){
constraints.gridx = i;
constraints.gridy = 0;
JLabel card = new JLabel(holeCards[i].getImageIcon());
add(card, constraints);
}
}
validate();
repaint();
}
any ideas?
Thanks
EDIT
solved:
It turns out that the HoleCardsPanel wasn't adding to its parent frame properly. Once that was fixed the adding of new JLabels works fine. I also:
added the call to the update() method to the event dispatch thread using SwingUtillities.invokeLater
had to call validate() from the uppermost component (in this case the JFrame) as Devon_C_Miller suggests in his answer.
It depends on what you want to happen and what layout managers are in use, but the basic rules are:
Make sure update is called on the EDT. If it's not (SwingUtilities.isEventDispatchThread()returns false) you will need to use SwingUtilities.invokeLater to schedule the update on the EDT. For example:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
update();
}});
Call invalidate(). Most things that change a component will do this for you. So you only need to call this if the following does not work on its own.
Call validate() on the highest affected component. This is probably the muddiest bit of Java's rendering cycle. The call to invalidate marks the component and all of its ancestors as needing layout. The call to validate performs the layout of the component and all of its descendants. One works "up" and the other works "down". You need to call validate on the highest component in the tree that will be affected by your change.
Also, calling validate on a top-level component (JWindow, JDialog, JFrame) will not necessarily resize that component. To make that happen, you'll need to call pack() or setSize().
If your changes alter the size or position of containers, The resized containers will repaint, but they will not erase the space the used to occupy. Calling repaint() on the parent of the container will cause it to repaint the background, correcting the damage.
Try calling revalidate(); repaint is not what you want.
As per:
API Docs
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.
revalidate() is basically a invalidate() followed by a validate().
See this question.....
The call to validate() should work, although revalidate() may be sufficient; repaint() should not be required. You might compare what you're doing to this example that does dynamic GridBagLayout.
You could try calling updateUI() method.
Have you tried SetVisible() on the labels ?
Have you tried to add the objets at initialisation without any update ? If they don't show up there, they will never show up.
It turns out that the HoleCardsPanel wansn't adding to its parent frame properly, once that was fixed the adding of new JLabels works fine. I added call to the update() method to the event dispatch thread using SwingUtillities.invokeLater I additionaly had to call validate() from the uppermost component (in this case the JFrame)