1

I have a couple of JTextFields on a very simple JFrame. Field 1 gets the focus first. When the user enters a number and hits enter, I want the focus to switch to Field 2. To accomplish that, I have an ActionListener attached to Field 1. I also have a Document Listener attached to Field 1 that allows only integers to be input into this field.

The document listener was in place first, and it is working as I would like. I then added the ActionListener, and I think (but I'm not sure) that my code is correct for this. But, when I test the code, the enter key seems to be having no effect at all. I suspect that the problem is a conflict between the two listeners in the sense that the Document Listener may be "swallowing up" the enter key before the ActionListener can react to it.

My code follows. Am I right? How can I correct this? Thanks!

        txtNum1.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent e) {
                setAnswer(e);
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                setAnswer(e);
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                setAnswer(e);
            }

            private void setAnswer(DocumentEvent e) {
                if (txtNum1.getText().equals("")) {
                    num1 = 0;
                } else {
                    num1 = Integer.parseInt(txtNum1.getText());
                }
                calcAnswer(o);
            }
        });

        /* If the user hits the Enter key, we want the focus to shift to
         * the next text field */
        txtNum1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("here"); //never ouputs
                txtNum2.requestFocusInWindow();
            }
        });
AndroidDev
  • 20,466
  • 42
  • 148
  • 239
  • If the DocumentListener gets informed about the ENTER event, couldn't you just use that instead of the ActionListener? – Thomas Mar 02 '13 at 01:39
  • 2
    Please edit your question to include an [sscce](http://sscce.org/) that shows your approach. – trashgod Mar 02 '13 at 01:42
  • Maybe. That was what I was originally trying to do, but an earlier post on SO yielded reponses that suggested adding an action listener. Can you suggest how I can modify the doc listener to accomplish this? – AndroidDev Mar 02 '13 at 01:43
  • I'd avoid using the action listener to trmasfer focus in this way and rely on the focus sub system instead. Take a look at [this example](http://stackoverflow.com/questions/14902410/switching-jtextfields-by-pressing-enter-key/14902740#14902740) which discusses ways to change the focus keys and focus order of components – MadProgrammer Mar 02 '13 at 01:49
  • @thomas I could be wrong, but I can't see the document listener consuming the enter key – MadProgrammer Mar 02 '13 at 01:50
  • Ps You should be using a DocumentFilter instead of a DocumentListener. It's designed to allow you to filter the input into a document before it gets there, have a look at [this](http://www.jroller.com/dpmihai/entry/documentfilter) examples – MadProgrammer Mar 02 '13 at 01:52
  • 1
    You suspect the DocumentListener, but have you checked that it is the case? To check if the DocumentListener is responsible to the ActionListener not firing, remove it temporarily, and see if it works. – Cyrille Ka Mar 02 '13 at 01:56
  • Great comment, Cyrille. I just did that and found that when I commented out the DocumentListener, the ActionListener still didn't fire. Now I'm really confused since this code seems quite straightforward. Why won't it work??? – AndroidDev Mar 02 '13 at 02:00
  • @usr55410 It sounds like you're using a `JTextArea` instead of a `JTextField` – MadProgrammer Mar 02 '13 at 02:02
  • Thanks, Mad, but I'm definitely using a JTextField. I just checked it. – AndroidDev Mar 02 '13 at 02:05
  • @usr55410 Can you post a running example. It could be the fields your attaching listeners to aren't the ones your putting on the screen... – MadProgrammer Mar 02 '13 at 02:06
  • Please do have a look at this wonderful thingy called [NavigationFilter](http://docs.oracle.com/javase/7/docs/api/javax/swing/text/NavigationFilter.html), as suggested by @kleopatra. This suggestion might can fit into your needs somewhere :-) – nIcE cOw Mar 02 '13 at 04:01

2 Answers2

2

Without a working example, it's impossible to accurately diagnose you problem.

However, from you description, I would recommend that you drop the ActionListener in favor of using the Focus SubSystem.

The following example demonstrates how to change the focus transfer keys as well as DocumentFilter (but I think either a DocumentListener or FocusListener would sufice for performing your calculation method)

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;

public class TestDocumentFilter03 {

    public static void main(String[] args) {
        new TestDocumentFilter03();
    }

    public TestDocumentFilter03() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public static class TestPane extends JPanel {

        private JTextField field1 = createTextField("1");
        private JTextField field2 = createTextField("2");
        private JTextField field3 = createTextField("3");
        private JTextField field4 = createTextField("4");
        private JTextField field5 = createTextField("5");
        private JTextField field6 = createTextField("6");
        private JTextField field7 = createTextField("7");
        private JTextField field8 = createTextField("8");
        private JTextField field9 = createTextField("9");

        public TestPane() {
            // Set up enter for focus transfer...
            Set forwardKeys = getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
            Set newForwardKeys = new HashSet(forwardKeys);
            newForwardKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
            setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
                            newForwardKeys);

            setLayout(new GridBagLayout());

            add("Field #1: ", field1, 0, 0);
            add("Field #2: ", field2, 2, 0);
            add("Field #3: ", field3, 4, 0);
            add("Field #4: ", field4, 6, 0);
            add("Field #5: ", field5, 8, 0);
            add("Field #6: ", field6, 2, 1);
            add("Field #7: ", field7, 4, 1);
            add("Field #8: ", field8, 6, 1);
            add("Field #9: ", field9, 8, 1);
        }

        protected static JTextField createTextField(String text) {
            JTextField field = new JTextField(text, 10);
            ((AbstractDocument)field.getDocument()).setDocumentFilter(new IntegerFilter());
            return field;
        }

        protected void add(String label, JTextField field, int x, int y) {
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridy = y;
            gbc.gridx = x;
            gbc.insets = new Insets(2, 2, 2, 2);
            gbc.anchor = GridBagConstraints.EAST;
            add(new JLabel(label), gbc);
            gbc.gridx++;
            add(field, gbc);
        }

    }

    public static class IntegerFilter extends DocumentFilter {

        @Override
        public void insertString(DocumentFilter.FilterBypass fb, int offset,
                                                         String string, AttributeSet attr)
                        throws BadLocationException {

            StringBuilder buffer = new StringBuilder(string);
            for (int i = buffer.length() - 1; i >= 0; i--) {
                char ch = buffer.charAt(i);
                if (!Character.isDigit(ch)) {
                    buffer.deleteCharAt(i);
                }
            }
            super.insertString(fb, offset, buffer.toString(), attr);
        }

        @Override
        public void replace(DocumentFilter.FilterBypass fb,
                                                int offset, int length, String string, AttributeSet attr) throws BadLocationException {
            if (length > 0) {
                fb.remove(offset, length);
            }
            insertString(fb, offset, string, attr);
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
-1

I think the problem with your code is that pressing the ENTER key is not registered as an ActionEvent. Rather, pressing the ENTER key can be detected as a KeyEvent. As a result, RATHER THAN IMPLEMENTING the event handler as follows:

/* If the user hits the Enter key, we want the focus to shift to
         * the next text field */
        txtNum1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                System.out.println("here"); //never ouputs
                txtNum2.requestFocusInWindow();
            }

you will want to do the following instead:

txtNum1.addKeyListener(new KeyListener() {
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_ENTER) {
            System.out.println("here");
            txtNum2.requestFocusInWindow();
        }
    }

Of course, my answer originated partly from the following source: http://www.rgagnon.com/javadetails/java-0253.html

Justin
  • 742
  • 5
  • 17
  • 34
  • -1, that is an old posting for AWT. As a general rule when Using Swing you should be using Key Bindings to listen for events like this. However, in this case MadProgrammer has already given better solutions for this requirement. – camickr Mar 02 '13 at 17:05