Действие JButton вызвало событие focusLost. Как это возможно? - PullRequest
5 голосов
/ 22 ноября 2011

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

Вот код:

btn.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        popup.show(btn, 3, btn.getHeight());
    }
});

Примечания:

  • btn - конечная локальная переменная типа JButton.
  • popup - конечная локальная переменная типа JPopupMenu.

Было сгенерировано следующее исключение:

java.awt.IllegalComponentStateException: component must be showing on the screen to determine its location
    at java.awt.Component.getLocationOnScreen_NoTreeLock(Unknown Source)
    at java.awt.Component.getLocationOnScreen(Unknown Source)
    at javax.swing.JPopupMenu.show(Unknown Source)
    at fr.def.iss.vd2.mod_site_watcher_gui.SiteElementPanel$4.actionPerformed(SiteElementPanel.java:117)
    at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
    at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonListener.focusLost(Unknown Source)
    at java.awt.Component.processFocusEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
    at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$000(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$2.run(Unknown Source)
    at java.awt.EventQueue$2.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

Насколько я понимаю, метод show жалуется, что btn не отображается.Как это возможно, что btn не отображается, когда вызывается его метод actionPerformed?

Самое странное в этой трассировке стека состоит в том, что метод actionPerformed кажется активированным, когда FocusEvent находится в процессеобработано (focusLost, на самом деле).

Вопрос: вы можете объяснить, как эта трассировка стека может произойти?

Эпилог

Благодаря предложению от trashgod,Я обнаружил проблему.

В Windows , когда кнопка исчезает во время ее нажатия, тогда ее ActionListener запускаются , как если бы кнопка была нажата.Такое поведение наблюдается в Windows, но не в Linux.

Я подал ошибку в базу данных ошибок Oracle / Sun.вот ссылка:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7115421

(эта ссылка вступит в силу через несколько дней после ее проверки командой Java).

Спасибо за вашеПомогите.Ответы от trashgod и Томаса очень помогли.

Ответы [ 5 ]

6 голосов
/ 23 ноября 2011

Одним из возможных источников является состояние гонки, которое позволяет событию сработать до того, как получатель станет видимым.Убедитесь, что ваши объекты Swing GUI созданы и обработаны только в потоке отправки событий .В статье Отладка Swing, окончательная сводка цитируется в Как генерировать исключения из RepaintManager , упоминается несколько подходов к автоматизации поиска.

3 голосов
/ 22 ноября 2011

• btn - конечная локальная переменная типа JButton.

Может быть, в этом проблема. Возможно, у вас есть ссылка на компонент, который не отображается на экране.

Вместо этого вы должны использовать:

JButton button = (JButton)e.getSource();

Тогда вы точно знаете, что ссылаетесь на компонент, сгенерировавший событие.

Также убедитесь, что у вас нет одноименных переменных класса.

2 голосов
/ 22 ноября 2011

Глядя на исходный код DefaultButtonModel#setPressed(...), мы видим следующее:

if(!isPressed() && isArmed()) {
        ...
        fireActionPerformed(
            new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
                            getActionCommand(),
                            EventQueue.getMostRecentEventTime(),
                            modifiers));
} 

Как вы можете видеть, ActionEvent срабатывает, когда кнопка была «вооружена», т.е.не нажата.Это соответствует событию «FocusLost».

0 голосов
/ 23 ноября 2011

Я не могу устоять, я только предпочитаю PopupFactory

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;

public class UsePopupFactory {

    private JFrame frame = new JFrame("PopupFactory Sample");
    private PopupFactory factory = PopupFactory.getSharedInstance();
    private Popup popup;

    public UsePopupFactory() {
        JPanel btnPanel = new JPanel();
        btnPanel.setBorder(new EmptyBorder(20, 20, 20, 20));
        btnPanel.setLayout(new GridLayout(0, 3));
        ActionListener actionListener = new ShowPopup(frame);
        JButton start3 = new JButton("Pick Me for Popup");
        JButton start = new JButton("Pick Me for Popup");
        JButton start2 = new JButton("Pick Me for Popup");
        btnPanel.add(start3);
        btnPanel.add(start);
        btnPanel.add(start2);
        start3.setVisible(false);
        start2.setVisible(false);
        start.addActionListener(actionListener);
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(btnPanel, BorderLayout.SOUTH);
        frame.setSize(new Dimension(d.width / 4, d.height / 4));
        frame.setVisible(true);
    }

    private class ShowPopup implements ActionListener {

        private Component component;

        ShowPopup(Component component) {
            this.component = component;
        }

        public synchronized void actionPerformed(ActionEvent actionEvent) {
            JPanel pnl = new JPanel();
            JComboBox combo = new JComboBox();
            JButton button = new JButton("any action");
            pnl.add(combo);
            pnl.add(button);
            pnl.setPreferredSize(new Dimension(250, 40));
            popup = factory.getPopup(component, pnl,
                    frame.getWidth() / 2 - pnl.getPreferredSize().width / 2,
                    frame.getHeight() / 2 - pnl.getPreferredSize().height / 2);
            popup.show();
            Timer timer = new Timer(3000, hider);
            timer.start();
        }
    }
    private Action hider = new AbstractAction() {

        private static final long serialVersionUID = 1L;

        @Override
        public void actionPerformed(ActionEvent e) {
            popup.hide();
        }
    };

    public static void main(final String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                UsePopupFactory uPF = new UsePopupFactory();
            }
        });
    }
}
0 голосов
/ 22 ноября 2011

Вместо этого используйте действие, специфичное для режима:

final JButton button = new JButton();
button.addMouseListener(new MouseAdapter(){
    public void mouseClicked(MouseEvent e) {
       popup.show(btn, 3, btn.getHeight());
    }
})
...