Created
June 12, 2009 10:51
-
-
Save fdb/128574 to your computer and use it in GitHub Desktop.
Examining Swing's container events.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package events; | |
import javax.swing.*; | |
import java.awt.*; | |
import java.awt.event.ActionEvent; | |
import java.awt.event.ActionListener; | |
import java.awt.event.ContainerEvent; | |
import java.awt.event.ContainerListener; | |
/** | |
* Demonstrates working with container events. | |
* <p/> | |
* I want a reliable mechanism to know if a button is going to be removed, so it | |
* can act on it. It turns out this can be done using addNotify() and removeNotify(). | |
* <p/> | |
* The previous implementation uses the ContainerListener, which provides | |
* a componentAdded and componentRemoved events, which sounded adequate enough. | |
* <p/> | |
* Removing and adding components to the content panel seemed to work well enough: | |
* events get called, the buttons remove their own listener when removed from the container, | |
* and they get gc'ed properly. | |
* <p/> | |
* The system broke down when you remove a panel that is one level above the button. In our | |
* example, this is the contentPanel. Removing that from the MainPanel doesn't trigger any events | |
* at all on the buttons. It does call finalize on the buttons and the panel, but the componentRemoved | |
* events never get called. | |
* <p/> | |
* Also, if we don't set the contentPanel to null, the buttons don't get finalized at all. This is because | |
* they are still part of the contentPanel. | |
* <p/> | |
* The lesson here is that you should really use the notify methods instead of the container listener, | |
* which is unusable in all but the simplest cases, and is actually used for other purposes. | |
*/ | |
public class MainPanel extends JPanel { | |
private int counter = 0; | |
private JPanel contentPanel; | |
public MainPanel() { | |
setLayout(new BorderLayout(5, 5)); | |
JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5)); | |
JButton addButton = new JButton("Add"); | |
addButton.addActionListener(new ActionListener() { | |
public void actionPerformed(ActionEvent e) { | |
addControl(); | |
} | |
}); | |
JButton removeButton = new JButton("Remove"); | |
removeButton.addActionListener(new ActionListener() { | |
public void actionPerformed(ActionEvent e) { | |
removeControl(); | |
} | |
}); | |
JButton removeAllButton = new JButton("Remove All"); | |
removeAllButton.addActionListener(new ActionListener() { | |
public void actionPerformed(ActionEvent e) { | |
removeAllControls(); | |
} | |
}); | |
JButton removePanelButton = new JButton("Remove Panel"); | |
removePanelButton.addActionListener(new ActionListener() { | |
public void actionPerformed(ActionEvent e) { | |
removePanel(); | |
} | |
}); | |
JButton gcButton = new JButton("GC"); | |
gcButton.addActionListener(new ActionListener() { | |
public void actionPerformed(ActionEvent e) { | |
garbageCollect(); | |
} | |
}); | |
contentPanel = new ContentPanel(new FlowLayout(FlowLayout.LEFT, 5, 5)); | |
controlPanel.add(addButton); | |
controlPanel.add(removeButton); | |
controlPanel.add(removeAllButton); | |
controlPanel.add(removePanelButton); | |
controlPanel.add(gcButton); | |
add(controlPanel, BorderLayout.NORTH); | |
add(contentPanel, BorderLayout.CENTER); | |
} | |
private void garbageCollect() { | |
System.gc(); | |
} | |
/** | |
* Add a control. | |
* <p/> | |
* This should add a container listener to the parent (contentPanel). | |
*/ | |
private void addControl() { | |
CarefulButton b = new CarefulButton(contentPanel, "button " + (++counter)); | |
contentPanel.add(b); | |
contentPanel.revalidate(); | |
contentPanel.repaint(); | |
} | |
/** | |
* Remove the last created control. | |
* <p/> | |
* This should remove the button from the container, and also trigger an event on the button. | |
* The button then removes itself from the container listeners. | |
* <p/> | |
* If there are no more controls left, this method will throw an exception. | |
*/ | |
private void removeControl() { | |
Component c = contentPanel.getComponent(contentPanel.getComponentCount() - 1); | |
contentPanel.remove(c); | |
contentPanel.revalidate(); | |
contentPanel.repaint(); | |
} | |
/** | |
* Removes all controls. | |
* <p/> | |
* This method tests if removing all controls will trigger events on all the buttons. | |
*/ | |
private void removeAllControls() { | |
contentPanel.removeAll(); | |
contentPanel.revalidate(); | |
contentPanel.repaint(); | |
} | |
/** | |
* Removes the panel the buttons are on. | |
* <p/> | |
* This method presents the logical flaw. When removed, none of the buttons receive an event, | |
* because they are not removed from the contentPanel at all. The panel is kept intact, it is | |
* only removed itself from its parent. | |
*/ | |
private void removePanel() { | |
remove(contentPanel); | |
contentPanel = null; | |
revalidate(); | |
repaint(); | |
} | |
public class ContentPanel extends JPanel { | |
public ContentPanel(LayoutManager layout) { | |
super(layout); | |
} | |
@Override | |
protected void finalize() throws Throwable { | |
System.out.println("Finalizing contentPanel."); | |
super.finalize(); | |
} | |
} | |
public class CarefulButton extends JButton implements ContainerListener { | |
public CarefulButton(Container parent, String text) { | |
super(text); | |
parent.addContainerListener(this); | |
} | |
@Override | |
public void addNotify() { | |
System.out.println(getText() + ": addNotify"); | |
} | |
@Override | |
public void removeNotify() { | |
System.out.println(getText() + ": removeNotify"); | |
} | |
public void componentAdded(ContainerEvent e) { | |
System.out.println(getText() + ": componentAdded event"); | |
if (e.getChild() != this) return; | |
System.out.println(getText() + ": I got added " + e); | |
} | |
public void componentRemoved(ContainerEvent e) { | |
System.out.println(getText() + ": componentRemoved event"); | |
if (e.getChild() != this) return; | |
System.out.println(getText() + ": I got removed" + e); | |
// Removing myself from the listeners. | |
e.getContainer().removeContainerListener(this); | |
} | |
@Override | |
protected void finalize() throws Throwable { | |
System.out.println(getText() + ": finalizing."); | |
super.finalize(); | |
} | |
} | |
public static void main(String[] args) { | |
JFrame frame = new JFrame("Events"); | |
MainPanel mainPanel = new MainPanel(); | |
frame.getContentPane().add(mainPanel); | |
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
frame.setSize(800, 800); | |
frame.setLocationByPlatform(true); | |
frame.setVisible(true); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment