默认按钮不会导致 JFormattedTextField 提交当前编辑

Default Button doesn't cause JFormattedTextField to commit current edit

我想知道我期望 JFormattedTextField 与默认按钮结合使用的方式是否正确。在编辑 JFormattedTextField 的值时,您通常希望提交该值然后使用它,这通常发生在 focusLost 或有时手动触发 ActionListener 时。现在,如果这样的文本字段位于定义了 DefaultButtonJDialog 中,则使用 Ctrl+Enter 触发默认按钮不会导致当前聚焦的 JFormattedTextField 触发它的 focusLost事件。

现在,我看到两个解决方案:

  1. 手动确保所有 inupt 字段都处于提交(或还原)状态
  2. 在默认按钮ActionListener中使用一个SwingUtilities.InvokeLater

但是,这两个看起来都很脏,尤其是第一个,因为在不忘记处理组件的情况下扩展所述对话框并不是很容易。第二个在我看来是相当不受欢迎的,因为更多的 invokeLater 通常意味着更多的代码更难调试。

有没有更好的方法解决这个问题?

示例:

import java.awt.BorderLayout;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class DefaultButton
{
  public static void main( String[] args )
  {
    JFrame frame = new JFrame();

    JFormattedTextField field = new JFormattedTextField();
    field.setValue( "HELLO" );

    JButton defaultButton = new JButton( "Default" );
    defaultButton.addActionListener( __ ->
    {
      JOptionPane.showMessageDialog( frame, "You've chosen: " + field.getValue() );
    } );
    frame.getRootPane().setDefaultButton( defaultButton );

    JPanel panel = new JPanel( new BorderLayout() );
    panel.add( field, BorderLayout.NORTH );
    panel.add( defaultButton, BorderLayout.SOUTH );
    frame.add( panel );

    frame.pack();
    frame.setLocationRelativeTo( null );
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
    frame.setVisible( true );
  }
}

是的,当 JFormattedTextField 具有输入焦点并且您激活 defaultButton 时,JFormattedTextField 不会失去焦点,因此不会提交该值。

参考How to Use Formatted Text Fields.
这是一个引用:

A formatted text field's text and its value are two different properties, and the value often lags behind the text.
The text property is defined by the JTextField class. This property always reflects what the field displays. The value property, defined by the JFormattedTextField class, might not reflect the latest text displayed in the field. While the user is typing, the text property changes, but the value property does not change until the changes are committed.

我看到了两种可能的解决方案(尽管我想还有更多)。
actionPerformed()方法中,要么调用getText()而不是getValue(),要么在调用getValue()之前调用commitEdit(),即

JOptionPane.showMessageDialog( frame, "You've chosen: " + field.getText() );

try {
    field.commitEdit();
}
catch (java.text.ParseException x) {
    x.printStackTrace();
}
JOptionPane.showMessageDialog(frame, "You've chosen: " + field.getValue());

编辑

我相信这就是您要找的。

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.KeyboardFocusManager;

import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class DefaultButton
{
  public static void main( String[] args )
  {
    JFrame frame = new JFrame();

    JFormattedTextField field = new JFormattedTextField();
    field.setValue( "HELLO" );

    JButton defaultButton = new JButton( "Default" );
    defaultButton.addActionListener( __ ->
    {
        KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
        if (kfm.getFocusOwner() == field) {
            kfm.focusNextComponent();            
        }
        EventQueue.invokeLater(() -> JOptionPane.showMessageDialog( frame, "You've chosen: " + field.getValue() ));
    } );
    frame.getRootPane().setDefaultButton( defaultButton );

    JPanel panel = new JPanel( new BorderLayout() );
    panel.add( field, BorderLayout.NORTH );
    panel.add( defaultButton, BorderLayout.SOUTH );
    frame.add( panel );

    frame.pack();
    frame.setLocationRelativeTo( null );
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
    frame.setVisible( true );
  }
}

专注于方法actionPerformed()。如果 JFormattedTextField 当前有焦点,则强制焦点移动到下一个组件。无论哪个组件具有键盘焦点,都将 JOptionPane 的显示包装在 invokeLater() 调用中。