Слушайте, когда компонент отображается в первый раз
Я пытаюсь захватить первый момент, когда компонент отображается на экране, не используя "грязные" решения, как с использованием таймера.
В принципе, я хочу знать момент, когда я могу с уверенностью начать использовать 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);
}
}
Спасибо заранее.
Ответы
Ответ 1
Причина, по которой компонент ComponentListener не работает, заключается в том, что он сообщает об изменениях в видимом свойстве - и по умолчанию это правда, даже не являясь частью иерархии компонентов.
Чтобы быть надежно уведомленным, используйте HierarchyListener
Редактируйте (размышляя о моей эволюции знаний в отношении этого вопроса/ответов, не уверен, что должен сказать сетевой этикет об этом... просто наведите меня, если это неправильный путь: -)
Во-первых: вопрос, заданный в теме, не обязательно связан с реальной проблемой (как прокомментировал Боро ниже - каким-либо образом ссылаться на комментарий?): нет необходимости держать какой-то локальный флаг, чтобы решить, или нет, это безопасно отправить getLocationOnScreen в компонент, просто спросите сам компонент. Learn-item 1 для себя:-)
Второе: вопрос, который задают, довольно интересен. Пять экспертов (включая меня, самопровозглашенных), пять разных ответов. Который вызвал немного копания с моей стороны.
Моя гипотеза: ComponentEvents не полезны для уведомления (first-) показов. Я знал, что componentShown бесполезен, потому что это своего рода свойство. Измените уведомление о видимом свойстве компонента (который редко изменяется). Однако недоумевая о рекомендуемой полезности перемещенного/измененного размера.
Построение прецедента: полностью подготовьте фрейм в примере и сохраните его для последующего отображения, типичный подход для улучшения воспринимаемой производительности. Мое предсказание, основанное на моей гипотезе: изменение размера/перемещение было произведено во время подготовки, ничто в шоу-времени (примечание: 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 для пакета... и voila, уведомление на этапе подготовки, без уведомления в шоу-время, снова рад. Игра немного больше: похоже, что компонент ComponentEvents запущен, если компонент отображается, что может или не может быть полезным в некоторых контекстах, но если показ - это состояние, за которым мы после.
Новые императивные правила (черновик):
Не используйте ComponentListener для уведомления о "показе". Это осталось от AWT-возраста.
Используйте AncestorListener. Это, похоже, замена Swing, слегка неверное уведомление о "добавлении", которое на самом деле означает "показ"
Используйте HierarchyListener только в том случае, если действительно заинтересованы в изменениях мелкозернистого состояния.
Ответ 2
Я использую AncestorListener и обрабатываю событие ancestorAdded.
Ответ 3
Как ни странно, 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);
}
}
Ответ 4
Использование 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
}
});
Ответ 5
Для большинства целей вы можете пойти с первым вызовом ComponentListener.componentMoved
(или если вас также интересует размер ComponentListener.componentResized
). Они вызываются всякий раз, когда изменяется положение/размер компонента.
Ответ 6
static ComponentListener cL = new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
super.componentResized(e);
System.out.println("componentShown = "+e.getComponent().isDisplayable());
}
};