JButton的动作调用了focusLost事件。这怎么可能?

时间:2011-11-22 15:29:58

标签: java swing focus action jbutton

我们的一位客户在我们的申请中报告了一个例外情况。问题是,我完全无法理解如何重现这个错误。

以下是代码:

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

注意:

  • btnJButton类型的最终本地变量。
  • popupJPopupMenu类型的最终本地变量。

抛出以下异常:

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 bug数据库中提交了一个错误。这是链接:

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

(此链接将在Java团队审核后的几天内生效。)

感谢您的帮助。来自trashgod和Thomas的答案帮了很多忙。

5 个答案:

答案 0 :(得分:6)

一种可能的来源是竞争条件,允许在收件人可见之前触发事件。验证是否在event dispatch thread上构造和操作了的Swing GUI对象。 Debugging Swing, the final summary中引用的文章How to generate exceptions from RepaintManager提到了自动搜索的几种方法。

答案 1 :(得分:3)

  

•btn是JButton类型的最终局部变量。

也许这就是问题所在。也许你有一个在屏幕上看不到的组件的引用。

相反,你应该使用:

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

然后,您确定要引用生成事件的组件。

Alsom确保您没有相同名称的类变量。

答案 2 :(得分:2)

查看DefaultButtonModel#setPressed(...)的源代码,我们会看到以下内容:

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

正如您所看到的,当按钮处于“布防”状态时,ActionEvent被触发,即有焦点但没有按下。这与“FocusLost”事件一致。

答案 3 :(得分:0)

改为使用模式特定操作:

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

答案 4 :(得分:-1)

我无法抗拒,我只能预感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();
            }
        });
    }
}