首次显示组件时监听

我试图抓住屏幕上显示组件的第一时刻,而不使用“脏”解决方案,就像使用计时器一样。 基本上,我想知道我可以安全地开始在组件上使用getLocationOnScreen()方法的那一刻。

我认为组件监听器可以帮助但在这里没有运气。 我现在卡住了,不知道哪个听众可以使用。 有什么建议么?

下面是一些示例代码,显示组件侦听器失败。

 import java.awt.Color; import java.awt.Dimension; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; public class CompListenerTest { static ComponentListener cL = new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { super.componentShown(e); System.out.println("componentShown"); } }; static MouseListener mL = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { super.mousePressed(e); JComponent c = (JComponent) e.getSource(); System.out.println("mousePressed c="+c.isShowing()); } }; public static void main(String[] args) { JPanel p = new JPanel(); p.setPreferredSize(new Dimension(300, 400)); p.setBackground(Color.GREEN); p.addComponentListener(cL); p.addMouseListener(mL); System.out.println("initial test p="+p.isShowing()); JPanel contentPane = new JPanel(); contentPane.setBackground(Color.RED); contentPane.add(p); JFrame f = new JFrame(); f.setContentPane(contentPane); f.setSize(800, 600); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } } 

提前致谢。

ComponentListener不起作用的原因是它报告对visible属性的更改 – 默认情况下也是如此,即使不是组件层次结构的一部分也是如此。

要可靠地通知,请使用HierarchyListener


编辑(关于这个问题/答案的关于我的知识演变的思考,不确定这样做的网络礼仪是什么…只是指导我,如果这是错误的方式去:-)

第一:主题中提出的问题不一定与实际问题有关(如下面Boro评论的那样 – 任何链接到评论的方式?):没有必要保留某种本地标志来决定它是否存在将getLocationOnScreen发送到组件是安全的,只需询问组件本身。 为自己学习项目1 🙂

第二:问的问题非常有趣。 五位专家(包括我自己,自称),五个不同的答案。 这引发了我的一些挖掘。

我的假设:ComponentEvents对于(first-)显示的通知没有用。 我知道componentShown是无用的,因为它是一种propertyChange通知组件的可见属性(很少改变)。 但是,对于移动/resize的建议用途感到困惑。

构建用例:在示例中完全准备框架并为以后的显示做好准备,这是一种提高感知性能的典型方法。 我的预测 – 基于我的假设:在准备时resize/移动,在显示时没有任何东西(注意:isShowing是我们所追求的,也就是后者)。 在OP的示例中添加的片段:

  final JFrame f = new JFrame(); f.setContentPane(contentPane); f.setSize(800, 600); // f.pack(); JFrame controller = new JFrame("opener"); controller.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Action open = new AbstractAction("open/hide second") { @Override public void actionPerformed(ActionEvent e) { f.setVisible(!f.isVisible()); } }; controller.add(new JButton(open)); controller.pack(); controller.setVisible(true); 

失望:在准备时没有通知,在展会时间通知,只是需要,我的假设似乎错了;-)最后一次机会:交换setSize一包…瞧,准备时通知,没有通知表演时间,再次开心我。 玩一点:如果一个组件是可显示的,看起来会触发ComponentEvents,这在某些情况下可能有用,也可能没有用,但如果显示的是我们所处的状态则不会。 该

新的帝国规则(草案):
不要使用ComponentListener来“显示”通知。 这是从AWT时代遗留下来的。
请使用AncestorListener。 这似乎是Swing的替代品,略微误解了“添加”的通知,实际上意味着“显示”
仅在真正对细粒度状态更改感兴趣时才使用HierarchyListener

我已经使用了AncestorListener并处理了ancestorAdded事件。

奇怪的是, ComponentListener在应用于JFrame时工作得很好。 这是我看到它工作的改变的来源。

 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class CompListenerTest { static ComponentListener cL = new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { super.componentShown(e); System.out.println("componentShown"); } }; public static void main(String[] args) { JPanel p = new JPanel(); p.setPreferredSize(new Dimension(300, 400)); p.setBackground(Color.GREEN); System.out.println("initial test p="+p.isShowing()); JPanel contentPane = new JPanel(); contentPane.setBackground(Color.RED); contentPane.add(p); JFrame f = new JFrame(); f.addComponentListener(cL); f.setContentPane(contentPane); f.setSize(800, 600); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } } 
 static ComponentListener cL = new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { super.componentResized(e); System.out.println("componentShown = "+e.getComponent().isDisplayable()); } }; 

使用AncestorListener和ancestorAdded为我工作。 这是示例代码:

  addAncestorListener(new AncestorListener() { @Override public void ancestorRemoved(AncestorEvent event) {} @Override public void ancestorMoved(AncestorEvent event) {} @Override public void ancestorAdded(AncestorEvent event) { // component is shown here } }); 

在大多数情况下,您可以第一次调用ComponentListener.componentMoved (或者如果您还对ComponentListener.componentResized大小感兴趣)。 只要组件的位置/大小发生变化,就会调用它们。