【问题标题】:How to wait for radio button to get selected in lost focus event如何等待单选按钮在失去焦点事件中被选中
【发布时间】:2012-01-11 17:58:32
【问题描述】:

我有一个 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);
      }
    });
  }
}

【问题讨论】:

  • 好吧,没有真正的方法可以知道何时应该运行搜索或何时应该等待,因为用户可能会单击单选按钮。您需要使用计时器在用户最后一次击键后的一定时间后运行搜索,或者不这样做并让用户通过单击“搜索”按钮开始搜索。您不需要像这样玩弄EventQueue - 查看How to Use Swing Timers。当您检测到击键时,重新启动计时器
  • 我的解决方法是使单选按钮不可聚焦(setFocusable(false)),这样它们就不会导致文本字段失去焦点事件。仍然很好奇是否有其他解决方案。
  • 您也不需要这样做,您可以使用连接到搜索字段上的击键的计时器。
  • 但是触发搜索的不是搜索字段中的击键,而是该字段失去焦点时。
  • 我认为也许你不应该开始搜索焦点丢失,因为它显然不适合你。也许最好让用户根据需要设置字段,然后按“提交”按钮。

标签: java swing awt-eventqueue


【解决方案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 事件,并且他们在释放鼠标之前将鼠标从单选按钮上移开,则单选按钮的选中状态不会改变。

同样,即使您最初的实现睡眠 250 毫秒也不能保证正常工作,因为理论上用户可以按住鼠标超过 250 毫秒,这也会产生错误的值。

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

我想不出更好的方法。

编辑:

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

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() );
    }
});

基本上,在您单击单选按钮释放鼠标之前,您的处理代码不会执行。

【讨论】:

  • 感谢蛮力的想法,因为我将直接实现 Timer,你的 hack 确实有效,但是通过 Timer 处理整个当前 JVM 实例 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-05
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
  • 2022-11-30
相关资源
最近更新 更多