在 Swing 中为对话框设置初始焦点组件的正确方法是什么?



我有一个错误潜伏在弹出包含文本字段的JOptionPane后,焦点偶尔不会出现在我想要的文本字段上。

我最终将其归结为一个合理的例子:

import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Component;
public class FocusIssueTest {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // The real thing has many more components in there,
                // but I removed them for the demo.
                MyInputPane myInputPane = new MyInputPane();
                myInputPane.showDialog(null);
            }
        });
    }
    public static class MyInputPane extends JPanel {
        private final JTextField textField;
        protected MyInputPane() {
            textField = new JTextField();
            textField.selectAll();
            textField.setColumns(30);
            setLayout(new BorderLayout());
            add(textField, BorderLayout.CENTER);
        }
        public boolean showDialog(Component parentComponent) {
            final JOptionPane optionPane = new JOptionPane(
                this, JOptionPane.PLAIN_MESSAGE,
                JOptionPane.OK_CANCEL_OPTION);
            JDialog dialog = optionPane.createDialog(
                parentComponent, "Select a Thing");
            /* Attempted solution #1 - wait until the window is active
            dialog.addWindowListener(new WindowAdapter() {
                @Override
                public void windowActivated(WindowEvent event) {
                    textField.requestFocusInWindow();
                }
            });
            */
            /* Attempted solution #2 - camickr's RequestFocusListener
            textField.addAncestorListener(new AncestorListener() {
                @Override
                public void ancestorAdded(AncestorEvent event) {
                    JComponent component = event.getComponent();
                    component.requestFocusInWindow();
                    component.removeAncestorListener(this);
                }
                @Override
                public void ancestorRemoved(AncestorEvent event) {
                }
                @Override
                public void ancestorMoved(AncestorEvent event) {
                }
            });
            */
            /* Attempted solution #3 - HierarchyListener
            textField.addHierarchyListener(new HierarchyListener() {
                @Override
                public void hierarchyChanged(HierarchyEvent event) {
                    Component component = event.getComponent();
                    if ((HierarchyEvent.SHOWING_CHANGED &
                         event.getChangeFlags()) != 0 &&
                            component.isShowing()) {
                        component.requestFocusInWindow();
                        component.removeHierarchyListener(this);
                    }
                }
            });
            */
            // Attempted solution #4 - appears to work but can't be
            // right, because eww.
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            SwingUtilities.invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    textField.requestFocusInWindow();
                                }
                            });
                        }
                    });
                }
            });
            dialog.setVisible(true);
            Object selectedValue = optionPane.getValue();
            return selectedValue instanceof Integer &&
                   (int) selectedValue == 0;
        }
    }
}

尝试的解决方案 #1 到 #3 都无法将焦点放在文本字段中。尝试的解决方案 #4 有效,但必须使用三级嵌套SwingUtilities.invokeLater调用不可能是执行此操作的正确方法。

那么正确的方法是什么呢?

我注意到JOptionPane.showInputDialog'文本字段确实会获得焦点,所以显然有一种方法可以做到这一点。

我不知道

规范的解决方案,但您可以尝试一次调用来在窗口侦听器中的事件线程上排队。例如,

     dialog.addWindowListener(new WindowAdapter() {
        @Override
        public void windowActivated(WindowEvent event) {
           SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                 textField.requestFocusInWindow();
              }
           });
        }
     });

其他可能的"笨蛋"包括使用短的单运行摆动计时器。

您是否考虑过直接子类化/初始化JDialog而不是使用JOptionPane?

java之所以没有"setFocusInWindow",是因为在某些平台上,不可能直接"设置"焦点(就像请求它一样)。

对我来说,似乎对"setVisible()"的调用是在EDT上放置一个事件,以使窗口可见,这反过来又将焦点从文本字段转移开。

最新更新