我有一个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);
}
});
}
}
答案 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() );
}
});
基本上,只有在单击单选按钮时释放鼠标才会执行处理代码。