如何等待单选按钮在丢失焦点事件中被选中

时间:2011-12-02 18:25:31

标签: java swing awt-eventqueue

我有一个Swing程序,它根据一些文本字段的内容和一对单选按钮的设置(在一个按钮组中)进行搜索。当某些文本字段失去焦点时,程序将自动搜索。当通过单击其中一个单选按钮触发失去焦点事件时,会出现问题。在单选按钮isSelected()值已更改之前,文本字段上的丢失焦点事件将被处理,因此搜索将使用"错误" (即旧的)参数,而不是基于单选按钮的新设置的参数。

我尝试使用我自己的invokeWhenIdle方法调用搜索(如下所示),在事件队列稳定后运行搜索,但它仍然使用单选按钮的旧设置。

我唯一可行的解​​决方案是在运行搜索之前在丢失焦点事件中延迟250毫秒,以便单选按钮有时间更改。这很有效,但它会让UI看起来很迟钝。

有更好的想法吗?

public static void invokeWhenIdle(final int a_max_retry, final Runnable a_runnable) {
  if (a_max_retry <= 0) {
    throw new IllegalStateException("invokeWhenIdle: Could not run " + a_runnable);
  }

  // get the next event on the queue
  EventQueue l_queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
  AWTEvent l_evt = l_queue.peekEvent();
  if (l_evt == null) {
    // nothing left on the queue (but us), we can do it
    SwingUtilities.invokeLater(a_runnable);
  } else {
    // still something in the queue, try again
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        invokeWhenIdle(a_max_retry - 1, a_runnable);
      }
    });
  }
}

1 个答案:

答案 0 :(得分:1)

不是答案,而是对正在发生的事情的解释。也许它会引发一个想法......

问题在于mousePressed支持按钮模型,而mouseReleased实际上更改了模型的选定值。

执行FocusListener代码时,单选按钮模型处于未定义状态。即使您使用invokeLater将FocusListener代码添加到EDT的末尾,代码仍将在生成mouseReleased事件之前执行。

以下显示了如何对侦听器进行编码以处理此问题。它假设按钮的状态即将改变:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class FocusSSCCE extends JPanel
{
    public FocusSSCCE()
    {
        final JRadioButton radio = new JRadioButton("Radio");
        add( radio );
        radio.setMnemonic('R');

        JTextField textField = new JTextField(10);
        add( textField );

        JButton button = new JButton("Button");
        add( button );

        textField.addFocusListener( new FocusAdapter()
        {
            public void focusLost(FocusEvent e)
            {
                boolean isSelected = radio.isSelected();

                //  Assumes selected state will change

                if (radio.getModel().isArmed())
                    isSelected = !isSelected;

                System.out.println( isSelected );
            }
        });
    }

    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("FocusSSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new FocusSSCCE() );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowUI();
            }
        });
    }
}

然而,即使这种方法也无法保证有效。如果由于某种原因,用户在单选按钮上生成mousePressed事件,并且在释放鼠标之前将鼠标移离单选按钮,则单选按钮的选定状态不会更改。

同样地,即使你原来的睡眠时间为250ms的实现也无法保证工作,因为理论上用户可以按住鼠标超过250ms,这也会产生错误的值。

  

我的解决方法是使单选按钮不可聚焦

我想不出更好的方法。

编辑:

我只是想到了一个疯狂的解决方案。

textField.addFocusListener( new FocusAdapter()
{
    public void focusLost(FocusEvent e)
    {
        if (e.getOppositeComponent() instanceof JRadioButton)
        {
            final JRadioButton radio = (JRadioButton)e.getOppositeComponent();

            MouseListener ml = new MouseAdapter()
            {
                public void mouseReleased(MouseEvent e)
                {
                    System.out.println( radio.isSelected() );
                    radio.removeMouseListener(this);
                }
            };

            radio.addMouseListener( ml );
        }

        else
            System.out.println( radio.isSelected() );
    }
});

基本上,只有在单击单选按钮时释放鼠标才会执行处理代码。