【问题标题】:Prevent breaking of JTextField / JDialog with repeated transferFocus()通过重复 transferFocus() 防止破坏 JTextField / JDialog
【发布时间】:2015-06-19 12:40:01
【问题描述】:

我有一个非常奇怪的场景,不幸的是我无法阻止在我的 Swing 应用程序中发生。然而,当它发生时,它会对我产生重大影响。也许有人可以帮忙!

基本设置如下:

  • Linux 环境。
  • 一个 JFrame 中有多个 JTextField。
  • 当按下 Enter 键时,JTextFields 通过 transferFocus() 推送。
  • 离开需要按 Enter 键才能删除的字段之一时会弹出一个 JDialog。

导致该问题的情况如下:

  • 按下 Enter 键几秒钟。

当按下回车键时,焦点明显飞过不同的文本字段。显示对话框时,回车键将其关闭,导致焦点继续飞过文本字段。最终,在几秒钟之内,Java 就崩溃了。文本框立即停止响应击键 - 您根本无法在其中输入任何内容。除此之外,一切似乎都很正常 - 您可以点击并关注不同的文本框、关闭应用程序等。

我创建了一个简单的测试用例,您可以使用它来重现这种情况。

JFrame:

public class TestSwing extends JFrame {

    JTextField jtfText1, jtfText2, jtfText3;
    TextHandler handler = null;

    public TestSwing() {
        super("TextField Test Demo");
        Container container = getContentPane();
        container.setLayout(new FlowLayout());
        jtfText1 = new MyJTextField(10);
        jtfText2 = new MyJTextField(10);
        jtfText3 = new MyJTextField(10);
        container.add(jtfText1);
        container.add(jtfText2);
        container.add(jtfText3);
        handler = new TextHandler();
        jtfText3.addActionListener(handler);
        setSize(325, 100);
        setVisible(true);
    }
    private class TextHandler implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JOptionPane.showConfirmDialog(null, "wait!");
        }
    }
    public static void main(String args[]) {
        TestSwing test = new TestSwing();
        test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

自定义 JTextField:

public class MyJTextField extends JTextField {    
    public MyJTextField(int len) {
        super(len);
        addKeyListener(new KeyAdapter() {
              public void keyPressed(KeyEvent evt) {
                int key = evt.getKeyCode();
                if (key == KeyEvent.VK_ENTER)
                  transferFocus();
              }
        });
    }
}

提前回答任何潜在问题:

  • 必须使用 Enter 键来转移焦点。
  • Enter 键的垃圾邮件来自用户在键盘上留下了一些东西(这是在零售环境中,所以这种情况经常发生)。
  • 仅仅关闭并重新启动应用程序并不是一个真正的选择,因为没有鼠标插入计算机。应用程序会在启动时自动启动,这使得这种情况具有毁灭性,因为解决问题的唯一方法是重新启动机器。
  • 这些机器不是很强大(处理和内存),这会导致问题发生的速度比在开发机器上重新创建时要快得多。

这是 Java 中的错误吗?任何人都可以想出一种方法来防止这种情况发生吗?

我能阻止这种情况发生的最接近的方法是在 JDialog (我的已扩展)中调用 sleep(500) 在它关闭之前调用它,但这并不是一个很好的解决方案......

我在 JDK 1.6、1.7 和 1.8 中对此进行了测试。虽然在更高版本中文本框变得无响应需要更长的时间,但最终还是会发生。

提前致谢!

山德尔

【问题讨论】:

    标签: java swing jtextfield jdialog


    【解决方案1】:

    不要使用按键事件。 KeyEvent 通常用于 AWT。 Swing 有更新更好的 API 可供使用(在大多数情况下)。在这种情况下,JTextField 被设计为在按下 Enter 键时响应 ActionEvent。

    您可以尝试跟踪上次按下 Enter 的时间,并忽略似乎在操作系统重复率内调用的事件。我的重复率似乎在 35 毫秒左右:

    import java.awt.event.*;
    import javax.swing.*;
    
    public class MyJTextField extends JTextField
    {
        private static long lastTime = System.currentTimeMillis();
    
        public MyJTextField(int len)
        {
            super(len);
            addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent evt)
                {
    
                    long diff = evt.getWhen() - lastTime;
                    System.out.println(diff);
    
                    if (diff > 50)
                    {
                        transferFocus();
                    }
    
                    lastTime = evt.getWhen();
                }
            });
        }
    }
    

    【讨论】:

    • 这是个好主意,谢谢!简单、整洁、有效。不幸的是,我的 JDialog 也使用了 KeyListener,因为没有可单击的实际按钮,因此我将您的解决方案扩展为具有“系统范围”的 lastTime 变量,并将相同的概念应用于我的 JDialog 实现。
    猜你喜欢
    • 2015-07-06
    • 2016-01-23
    • 1970-01-01
    • 2011-05-27
    • 2015-01-12
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 2012-06-12
    相关资源
    最近更新 更多