【问题标题】:What is the proper way to set initially focused component for a dialog in Swing?在 Swing 中为对话框设置初始焦点组件的正确方法是什么?
【发布时间】:2015-01-25 15:05:05
【问题描述】:

在弹出包含文本字段的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' 文本字段确实获得焦点,所以显然有办法做到这一点。

【问题讨论】:

    标签: java swing focus joptionpane


    【解决方案1】:

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

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

    其他可能的“麻烦”包括使用短的单次运行 Swing 计时器。

    【讨论】:

    • 一个绝对行不通。大多数时候有两个工作,但似乎随机选择不工作。三个看起来还不错,但我的意思是,我怎么能保证它没问题?也许它失败的频率更低,但仍然失败。 :)
    • @Trejkaz:考虑查看JOptionPane.showInputDialog 的源代码,了解 Swing 编码人员是如何做到的。顺便说一句,你的问题问得很好。
    • 看起来他们在 BasicOptionPaneUI 中维护了一个名为 inputComponent 的字段。如果该字段不为空,则在显示对话框之前 将其聚焦。如果 wantInput 已设置为 true,他们会设置它,但否则无法设置我自己的值(但反射可能会起作用,并且有点诱人。)
    【解决方案2】:

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

    java没有“setFocusInWindow”的原因是因为在某些平台上无法直接“设置”焦点(只要请求它。)

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

    【讨论】:

    • 我已经考虑过了,但是 JOptionPane 包含很多用于 rtl 布局、可访问性和其他我不想复制的硬东西的代码。
    • 我倾向于远离 JOptionPane,因为当不得不模拟用户输入响应时,它会使编写单元测试变得困难 100 倍。对话框上的 setVisible 块,sun 会创建一个新的 EDT 以在对话框处于模态时处理事件。 soo ....创建一个componentListener并为您的文本字段覆盖您的“componentResized”方法(在调用setVisible(true)之后调用“Should”。由optionPane创建的对话框有自己的windowListener,它可以确定定义的焦点在 JOptionPane.initDialog( ) (
    • 你说得对,单元测试很痛苦。在工作中,我通过抽象询问信息的行为来解决这个特定问题。所以代码永远不会直接显示选项窗格,它总是类似于 selectThingDisplayer.ask(),很容易模拟出来进行测试。这也意味着,如果我以后必须从选项窗格中切换出来,我不必更改调用...
    • 这类似于我用于消息对话框和用户确认的方法(有一个包装 JOptionPane 的类,然后使用 DI 覆盖它以用于测试用例。)
    猜你喜欢
    • 1970-01-01
    • 2012-11-08
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-07
    相关资源
    最近更新 更多