Search code examples
javaswingjtextfield

Moving a focus to Next JTextfield when it crosses it Jtextfieldlimit


I'm working to design license activation part. It had two text Fields in Java Swing.

Java code:

jTextField1.setDocument(new JTextFieldLimit(8));   
jTextField2.setDocument(new JTextFieldLimit(8));

when the jTextField1 is ended up by entering 8 alphanumeric characters. It had to move its focus to next JTextField2 automatically.Are there any default methods available or how it can be done.


Solution

  • The basic answer to your question is, KeyboardFocusManager#focusNextCompnent

    The longer answer is slightly more complicated.

    Lets start with the fact that Java 1.4, it is no longer required or recommended that you utilise the Document for this functionality, instead use a DocumentFilter, see Implementing a Document Filter and DocumentFilter Examples for more details

    I also prefer reusable and configurable solutions, to this end, I'd prefer to see a solution which allowed me to change the decision making processes that are involved in deciding when focus should be transferred and configure how the transfer actually occurs

    Now, remember, you could use a custom FocusTraversalPolicy to change the way in which focus moves between the components, but this isn't always the desirable solution.

    Let's start with some basics...

        public interface AutoFocusTransferDelegate {
            public boolean shouldTransferFocus(Document doc);
        }
    
        public interface AutoFocusTransferObserver {
            public void focusTransferShouldOccur(Document doc);
        }
    
        public class AutoFocusTransferHandler implements DocumentListener {
    
            private AutoFocusTransferListener listener;
            private AutoFocusTransferDelegate delegate;
    
            public AutoFocusTransferHandler(AutoFocusTransferListener listener, AutoFocusTransferDelegate delegate) {
                this.listener = listener;
                this.delegate = delegate;
            }
    
            @Override
            public void insertUpdate(DocumentEvent e) {
                checkForTransfer(e.getDocument());
            }
    
            @Override
            public void removeUpdate(DocumentEvent e) {
                checkForTransfer(e.getDocument());
            }
    
            @Override
            public void changedUpdate(DocumentEvent e) {
                checkForTransfer(e.getDocument());
            }
    
            public void checkForTransfer(Document doc) {
                if (delegate != null && delegate.shouldTransferFocus(doc)) {
                    if (listener != null) {
                        listener.focusTransferShouldOccur(doc);
                    }
                }
            }
    
        }
    

    Here we have a "delegate", which is used to configure and customise the decisions making process over when a transfer should take place, a "observer" which is notified when a transfer should take place, which allows it to decide "how" the transfer should take place and a DocumentListener to monitor the Document which is used to query the delegate and notify the observer.

    Sure, you could wrap it all up in a single class, but now, you have a very simple and highly configurable solution where you don't need to extend from classes in order to achieve a new result (nit pick on my part). It's a basic example of composition over inheritance.

    And you might be able to use it something like...

    Focus me baby

    import java.awt.EventQueue;
    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;
    import java.awt.KeyboardFocusManager;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.event.DocumentEvent;
    import javax.swing.event.DocumentListener;
    import javax.swing.text.AbstractDocument;
    import javax.swing.text.AttributeSet;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.Document;
    import javax.swing.text.DocumentFilter;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            public TestPane() {
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridwidth = GridBagConstraints.REMAINDER;
    
                int maxCharacters = 8;
                AutoFocusTransferListener listener = new AutoFocusTransferListener() {
                    @Override
                    public void focusTransferShouldOccur(Document doc) {
                        KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
                    }
                };
                AutoFocusTransferDelegate delegate = new MaxLengthAutoFocusTransferDelegate(maxCharacters);
                DocumentFilter filter = new SizeFilter(maxCharacters);
                DocumentListener docListener = new AutoFocusTransferHandler(listener, delegate);
    
                add(createTextField(8, filter, docListener), gbc);
                add(createTextField(8, filter, docListener), gbc);
                add(new JButton("Ok"), gbc);
            }
    
            protected  JTextField createTextField(int maxCharacters, DocumentFilter filter, DocumentListener docListener) {
                JTextField field = new JTextField(maxCharacters);
                AbstractDocument doc = (AbstractDocument)field.getDocument();
                doc.setDocumentFilter(filter);
                doc.addDocumentListener(docListener);
                return field;
            }
    
        }
    
        public class MaxLengthAutoFocusTransferDelegate implements AutoFocusTransferDelegate {
    
            private int maxLength;
    
            public MaxLengthAutoFocusTransferDelegate(int maxLength) {
                this.maxLength = maxLength;
            }
    
            @Override
            public boolean shouldTransferFocus(Document doc) {
                return doc.getLength() >= maxLength;
            }
    
        }
    
        public class SizeFilter extends DocumentFilter {
    
            private int maxCharacters;
    
            public SizeFilter(int maxChars) {
                maxCharacters = maxChars;
            }
    
            public void insertString(FilterBypass fb, int offs, String str, AttributeSet a)
                    throws BadLocationException {
    
                if ((fb.getDocument().getLength() + str.length()) <= maxCharacters) {
                    super.insertString(fb, offs, str, a);
                }
            }
    
            public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
                    throws BadLocationException {
    
                if ((fb.getDocument().getLength() + str.length() - length) <= maxCharacters) {
                    super.replace(fb, offs, length, str, a);
                }
            }
        }
    
        public class AutoFocusTransferHandler implements DocumentListener {
    
            private AutoFocusTransferListener listener;
            private AutoFocusTransferDelegate delegate;
    
            public AutoFocusTransferHandler(AutoFocusTransferListener listener, AutoFocusTransferDelegate delegate) {
                this.listener = listener;
                this.delegate = delegate;
            }
    
            @Override
            public void insertUpdate(DocumentEvent e) {
                checkForTransfer(e.getDocument());
            }
    
            @Override
            public void removeUpdate(DocumentEvent e) {
                checkForTransfer(e.getDocument());
            }
    
            @Override
            public void changedUpdate(DocumentEvent e) {
                checkForTransfer(e.getDocument());
            }
    
            public void checkForTransfer(Document doc) {
                if (delegate != null && delegate.shouldTransferFocus(doc)) {
                    if (listener != null) {
                        listener.focusTransferShouldOccur(doc);
                    }
                }
            }
    
        }
    
        public interface AutoFocusTransferDelegate {
    
            public boolean shouldTransferFocus(Document doc);
        }
    
        public interface AutoFocusTransferListener {
    
            public void focusTransferShouldOccur(Document doc);
        }
    }