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

时间:2015-01-25 15:05:05

标签: java swing focus joptionpane

我有一个错误潜伏在哪里,在弹出包含文本字段的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'文本字段 获得焦点,所以显然有一种方法可以做到。

2 个答案:

答案 0 :(得分:2)

我不知道规范的解决方案,但您可以尝试在窗口侦听器中的事件线程上进行一次调用。例如,

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

其他可能" kludges"包括使用简短的单次运行Swing计时器。

答案 1 :(得分:2)

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

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

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