Слушайте, когда компонент отображается в первый раз - PullRequest
13 голосов
/ 25 апреля 2011

Я пытаюсь запечатлеть самый первый момент, когда компонент отображается на экране без использования «грязных» решений, как при использовании таймера.По сути, я хочу знать момент, когда я смогу безопасно начать использовать метод 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);
    }
}

Заранее спасибо.

Ответы [ 6 ]

15 голосов
/ 25 апреля 2011

Причина, по которой компонент ComponentListener не работает, заключается в том, что он сообщает об изменениях в видимом свойстве - и это верно по умолчанию, даже не будучи частью иерархии компонентов.

Чтобы получать достоверные уведомления, используйте HierarchyListener


Edit (размышления об эволюции моих знаний по этому вопросу / ответам, не уверен, что сетевой этикет говорит о том, как это делать... просто наведите меня, если это неправильный путь: -)

Во-первых: вопрос, заданный в теме, не обязательно связан с реальной проблемой (как прокомментировал Боро ниже - любой способ ссылки накомментарий?): нет необходимости сохранять какой-то локальный флаг, чтобы решить, безопасно ли отправлять getLocationOnScreen компоненту, просто спросите сам компонент.Изучите пункт 1 для себя :-)

Второе: заданный вопрос довольно интересный.Пять экспертов (в том числе и я самопровозглашенный), пять разных ответов.Что вызвало немного копания с моей стороны.

Моя гипотеза: ComponentEvents не полезны для уведомления о (первом) показе.Я знал, что componentShown бесполезен, потому что он является своего рода уведомлением propertyChange о видимом свойстве компонента (которое редко изменяется).Озадаченный предполагаемой полезностью перемещения / изменения размера, однако.

Создание варианта использования: полностью подготовить кадр в примере и сохранить его готовым для последующего показа, типичный подход для улучшения воспринимается спектакль.Мой прогноз - основанный на моей гипотезе: измененный размер / перемещенный уволен во время подготовки, ничего во время шоу (примечание: 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, только если действительно заинтересованы в детальных изменениях состояния

6 голосов
/ 25 апреля 2011

Я использую AncestorListener и обработал событие ancestorAdded.

3 голосов
/ 25 апреля 2011

Как ни странно, 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);
    }
}
2 голосов
/ 27 сентября 2014

Использование 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
        }
    });
2 голосов
/ 25 апреля 2011
static ComponentListener cL = new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            super.componentResized(e);
            System.out.println("componentShown = "+e.getComponent().isDisplayable());
        }
    };
1 голос
/ 25 апреля 2011

Для большинства целей вы можете перейти с первого звонка на ComponentListener.componentMoved (или, если вас также интересует размер ComponentListener.componentResized).Они вызываются всякий раз, когда изменяется позиция / размер компонента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...