Skip to content

Instantly share code, notes, and snippets.

@fdb
Created June 12, 2009 10:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fdb/128574 to your computer and use it in GitHub Desktop.
Save fdb/128574 to your computer and use it in GitHub Desktop.
Examining Swing's container events.
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