java 侦听组件何时首次显示

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5777754/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 12:43:50  来源:igfitidea点击:

Listen for when a Component is Shown for the First Time

javaswinglistener

提问by Boro

I am trying to capture the very first moment when a component is shown on the screen without using 'dirty' solutions as with use of a timer. Basically, I want to know the moment when I can safely start using getLocationOnScreen()method on the component.

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

I thought that component listener could help but no luck here. I am stuck for now and do not know which listener to use for this. Any suggestions?

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

Here is some sample code which shows that a component listener fails.

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

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);
    }
}

Thanks in advance.

提前致谢。

回答by kleopatra

The reason a ComponentListener doesn't work is that it reports changes to the visible property - and that is true by default, even without being part of the component hierarchy.

ComponentListener 不工作的原因是它报告了对可见属性的更改——默认情况下这是真的,即使不是组件层次结构的一部分。

To be reliably notified, use a HierarchyListener

要获得可靠的通知,请使用 HierarchyListener



编辑(思考我在这个问题/答案方面的知识演变,不确定网络礼节对这样做有什么看法......如果那是错误的方法,请指导我:-)

First: the question as asked in the subject is not necessarily related to the actual problem (as commented by Boro below - any way to link to a comment?): there's no need to keep some kind of local flag to decide whether or not it is safe to send a getLocationOnScreen to a component, simply ask the component itself. Learn-item 1 for myself :-)

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

Second: The question as asked is quite interesting. Five experts (including myself, self-proclaimed), five different answers. Which triggered a bit of digging on my part.

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

My hypothesis: ComponentEvents are not useful for notification of (first-)showing. I knew that componentShown is useless because it's a kind-of propertyChange notification of the visible property of a component (which rarely changes). Puzzled about the suggested usefulness of moved/resized, though.

我的假设:ComponentEvents 对(首次)显示的通知没有用。我知道 componentShown 没用,因为它是组件可见属性的一种 propertyChange 通知(很少更改)。不过,对移动/调整大小的建议用处感到困惑。

Constructing a use-case: fully prepare the frame in the example and keep it ready for later showing, a typical approach to improve perceivedperformance. My prediction - based on my hypothesis: resized/moved fired at prepare-time, nothing at show-time (note: the isShowing is what we are after, that is the latter). A snippet to add in the OP's example:

构建用例:为示例中的框架做好充分准备,并为以后的展示做好准备,这是提高感知性能的典型方法。我的预测 - 基于我的假设:在准备时调整大小/移动,在展示时没有任何东西(注意: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);

Disappointment: no notification at prepare-time, notification at show-time, just as needed, my hypothesis seemed wrong ;-) Last chance: swap the setSize for a pack ... and voila, notification at prepare-time, no notification at show-time, happy me again. Playing a bit more: looks like ComponentEvents are fired if the a component is displayable, which may or may not be useful in some contexts but not if showing is the state we are after. The

失望:在准备时没有通知,在展示时没有通知,就像需要一样,我的假设似乎是错误的;-) 最后机会:将 setSize 交换为一个包......瞧,准备时通知,没有通知表演时间,再次让我开心。多玩一点:看起来如果组件是可显示的,则 ComponentEvents 会被触发,这在某些上下文中可能有用也可能没有用,但如果显示是我们所追求的状态,则不会。这

New imperial rules (draft):
Do not use ComponentListener for notification of "showing". That's left-over from AWT-age.
Do use AncestorListener. That seems to be the Swing replacement, slightly misnomed notification of "added" which actually means "showing"
Do use HierarchyListener only if really interested in fine-grained state changes

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

回答by camickr

I"ve use an AncestorListener and handled the ancestorAdded event.

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

回答by Andrew Thompson

Oddly, the ComponentListenerworks just fine when applied to the JFrame. Here is the altered source where I saw it work.

奇怪的是,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);
    }
}

回答by AZDean

Using AncestorListener and ancestorAdded worked for me. Here's sample code:

使用祖先监听器和祖先添加对我有用。这是示例代码:

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
    }
});

回答by StanislavL

static ComponentListener cL = new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e) {
        super.componentResized(e);
        System.out.println("componentShown = "+e.getComponent().isDisplayable());
    }
};

回答by Howard

For most purposes you can go with the first call to ComponentListener.componentMoved(or if you are also interested in the size ComponentListener.componentResized). Those are called whenever the position/size of the component changes.

对于大多数用途,您可以使用第一次调用ComponentListener.componentMoved(或者如果您也对 size 感兴趣ComponentListener.componentResized)。每当组件的位置/大小发生变化时都会调用它们。