Prevent breaking of JTextField / JDialog with repeated transferFocus()

155 views Asked by At

I have a very strange scenario which unfortunately I cannot prevent from occurring in my Swing application. When it occurs however, it has major consequences for me. Perhaps somebody could help!

The basic setup is as follows:

  • Linux environment.
  • Multiple JTextFields in a JFrame.
  • JTextFields push through transferFocus() when the Enter key is pressed.
  • A JDialog pops up on leaving one of the fields which requires the Enter key to be pressed to remove it.

The situation that causes the issue is as follows:

  • The Enter key is held down for a few seconds.

When the enter key is held down, the focus obviously flies through the different text fields. When the dialog box is shown, the enter key closes it causing the focus to then continue to fly through the text fields. Eventually, within a couple of seconds, Java breaks. The textboxes immediately stop responding to key strokes - you cannot type anything in them at all. Other than that, everything seems normal - you can click around and focus on different textboxes, close the application etc.

I have created a simple test case you can use to recreate the situation.

The 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);
    }
}

The custom 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();
              }
        });
    }
}

To answer any potential questions up front:

  • The Enter key must be used to transfer the focus.
  • The spamming of the Enter key comes from the user leaving something on the keyboard (this is in the retail environment so this happens often).
  • Simply closing and restarting the application is not really an option as there is no mouse plugged into the computer. The application is booted up automatically on start-up making this scenario devastating as the only way to fix the problem is to restart the machine.
  • The machines aren't very powerful (processing & memory) which somehow causes the issue to happen a lot quicker than when it's recreated on a development machine.

Is this a bug in Java? Can anyone think of a way to prevent this from happening?

The closest I can get to preventing this from happening is to put a sleep(500) call in the JDialog (mine is extended) before it closes but that's not really a great fix...

I have tested this in JDK 1.6, 1.7 and 1.8. While it takes a bit longer in the later versions for the textboxes to become unresponsive, it still happens eventually.

Thanks in advance!

Xandel

1

There are 1 answers

1
camickr On BEST ANSWER

Don't use KeyEvents. KeyEvents are generally used in AWT. Swing has newer and better API's to use (in most cases). In this case a JTextField was designed to respond to an ActionEvent when the Enter key is pressed.

You could try to keep track of the last time Enter was pressed and ignore events that seem to be invoked within the repeat rate of the OS. My repeat rate appears to be around 35ms:

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();
            }
        });
    }
}