Say I have a simple JFrame with a JTabbedPane containing 3 panels, and the second panel contains a JComponent. Is there a way the JComponent to be notified when "Tab 2" panel is removed from its container? My problem is that the JComponent may be deep in the hierarchy.
Obviously, I am looking for SWING solution here... :)
,'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''`.
| |
| ,----------Y....................... |
| | Tab 1 | Tab 2 | Tab 3 | |
| :..........: :..................................... |
| | | |
| | | |
| | | |
| | +--------------------+ | |
| | |Some JComponent here| | |
| | +--------------------+ | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| `-----------------------------------------------------------' |
`-----------------------------------------------------------------
I tried to do this with the ancestorRemoved(), but no luck... I am obviously doing something wrong...
PS. the ASCII art is made with JavE.
I'd be use ComponentListener
for this job (CardLayout
is so close, similar in compare with JTabbedPane
), code example contains all related listeners (disclaimer --> notice blocking EDT works only as code example and in this form only)
Ancesor & HierarchyListener
are little bit asynchronous, maybe there is your problem to catch proper event from AncestorListener
my question about similar issue
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
public class CardlayoutTest extends JFrame {
private static final long serialVersionUID = 1L;
public CardLayout card = new CardLayout();
public CardlayoutTest() {
JPanel pnlA = new JPanel(new BorderLayout());
pnlA.add(new JButton("A"), BorderLayout.CENTER);
JPanel pnlB = new JPanel(new BorderLayout());
pnlB.add(new JButton("B"), BorderLayout.CENTER);
JPanel pnlC = new JPanel(new BorderLayout());
pnlC.add(new JButton("C"), BorderLayout.CENTER);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(card);
add(pnlA, "A");
add(pnlB, "B");
add(pnlC, "C");
pnlA.addAncestorListener(new EventHandler());
pnlB.addAncestorListener(new EventHandler());
pnlC.addAncestorListener(new EventHandler());
pnlA.addHierarchyListener(new EventHandler());
pnlB.addHierarchyListener(new EventHandler());
pnlB.addHierarchyListener(new EventHandler());
pnlA.addComponentListener(new EventHandler());
pnlB.addComponentListener(new EventHandler());
pnlB.addComponentListener(new EventHandler());
}
class EventHandler implements AncestorListener, ComponentListener, HierarchyListener {
@Override
public void ancestorAdded(AncestorEvent event) {
System.out.println("CardlayoutTest.EventHandler.ancestorAdded()");
}
@Override
public void ancestorMoved(AncestorEvent event) {
System.out.println("CardlayoutTest.EventHandler.ancestorMoved()");
}
@Override
public void ancestorRemoved(AncestorEvent event) {
System.out.println("CardlayoutTest.EventHandler.ancestorRemoved()");
}
@Override
public void hierarchyChanged(HierarchyEvent e) {
System.out.println("Components Change: " + e.getChanged());
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
if (e.getComponent().isDisplayable()) {
System.out.println("Components DISPLAYABILITY_CHANGED : " + e.getChanged());
} else {
System.out.println("Components DISPLAYABILITY_CHANGED : " + e.getChanged());
}
}
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
if (e.getComponent().isDisplayable()) {
System.out.println("Components SHOWING_CHANGED : " + e.getChanged());
} else {
System.out.println("Components SHOWING_CHANGED : " + e.getChanged());
}
}
}
public void componentHidden(ComponentEvent e) {
System.out.println(e.getComponent().getClass().getName() + " --- Hidden");
}
public void componentMoved(ComponentEvent e) {
System.out.println(e.getComponent().getClass().getName() + " --- Moved");
}
public void componentResized(ComponentEvent e) {
System.out.println(e.getComponent().getClass().getName() + " --- Resized ");
}
public void componentShown(ComponentEvent e) {
System.out.println(e.getComponent().getClass().getName() + " --- Shown");
}
}
public static void main(String[] args) {
CardlayoutTest t = new CardlayoutTest();
t.setSize(500, 500);
System.out.println("CardlayoutTest.main()------------------------ FIRST");
t.card.show(t.getContentPane(), "A");
t.setVisible(true);
System.out.print("\n");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("CardlayoutTest.main()------------------------ SECOND");
t.card.show(t.getContentPane(), "B");
System.out.print("\n");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("CardlayoutTest.main()------------------------ THIRD");
t.card.show(t.getContentPane(), "C");
System.out.print("\n");
}
}
Thanks to mKorbel's who posted a good example where he uses the HierarchyListener I came up with solution that I believe is satisfying my needs. In this case I use JLabel
as the component that needs to be notified.
I will accept mKorbel's answer simply because he saved me lots of time.
Here is the code:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.JTabbedPane;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
public class TestFrame extends JFrame {
private static final long serialVersionUID = 8388031406846751884L;
private JPanel contentPane;
private JTabbedPane tabbedPane;
private JLabel label; /// The component that needs to be notified when it should save its states.
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestFrame frame = new TestFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Adds necessary listeners.
* @param argLabel
*/
private void install(JLabel argLabel) {
// ::::: HierarchyListener ::::::::::::::::::::::::::::::::::::::::::::::::::::::::
argLabel.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent arg0) {
if ((arg0.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
if (!arg0.getComponent().isDisplayable()) {
// component is not displayable due to the panel being removed
doSomething();
}
}
}
});
// ::::: AncestorListener :::::::::::::::::::::::::::::::::::::::::::::::::::::::::
argLabel.addAncestorListener(new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent arg0) {
// not interested in this one
}
@Override
public void ancestorMoved(AncestorEvent arg0) {
// not interested in this one
}
@Override
public void ancestorRemoved(AncestorEvent arg0) {
// ancestorRemoved() is useful when user navigates between tabs, and causes
// component to become invisible.
doSomething();
}
});
}
public void doSomething() {
System.out.println("Saving some state(s)...");
}
/**
* Create the frame.
*/
public TestFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 693, 376);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
tabbedPane = new JTabbedPane(JTabbedPane.TOP);
// first panel
tabbedPane.add(new JPanel());
// second panel
JPanel panelWithComponents = new JPanel();
JPanel childPanel = new JPanel();
label = new JLabel("Test label");
install(label);
childPanel.add(label);
panelWithComponents.add(childPanel);
tabbedPane.add(childPanel);
// third panel
tabbedPane.add(new JPanel());
contentPane.add(tabbedPane, BorderLayout.CENTER);
JButton btnRemove = new JButton("Remove second");
btnRemove.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
tabbedPane.remove(1);
}
});
contentPane.add(btnRemove, BorderLayout.SOUTH);
} // main() method
} // TestFrame class
If it is the component itself which needs to do something, you can also override add/removeNotify (don't forget to call super though.
import java.awt.*;
import java.awt.event.*;
import java.util.Date;
import javax.swing.*;
public class LabelWithTimer extends JLabel {
private final Timer timer;
public LabelWithTimer() {
timer = new Timer(1000, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setText(new Date().toString());
}
});
}
@Override
public void addNotify() {
super.addNotify();
timer.start();
System.out.println("Clock started");
}
@Override
public void removeNotify() {
System.out.println("Clock stopped");
timer.stop();
super.removeNotify();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
final JPanel clock = new JPanel(new GridBagLayout());
clock.add(new LabelWithTimer());
final JTabbedPane tabs = new JTabbedPane();
tabs.addTab("Empty", new JLabel());
tabs.setPreferredSize(new Dimension(400, 300));
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(tabs);
frame.getContentPane().add(new JButton(new AbstractAction("Add/remove clock") {
@Override
public void actionPerformed(ActionEvent e) {
if(tabs.getTabCount() == 2) {
tabs.removeTabAt(1);
}
else {
tabs.addTab("Clock", clock);
tabs.setSelectedIndex(1);
}
}
}), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}