问题
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.
回答1:
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");
}
}
回答2:
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
回答3:
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);
}
});
}
}
来源:https://stackoverflow.com/questions/14833368/is-there-a-way-to-get-notification-when-jcomponent-is-not-visible-anymore-beucas