JavaIssues
JavaIssues

Reputation: 3

Java : Error in Cursor automatically move from one TextField to other

When I enter the TextField 7 character, the cursor moved automatically moved to next TextField. But in my code, my cursor did not goes to nextField. (I knew i can't use the addKeyListener, so tried to use DocumentListener for this code)

lbltext1  = new JLabel("Text1");
panel.add(lbltext1, "cell 0 1,alignx trailing");
final int maxSize =7 ;
for (int i = 0; i < 1; i++) {
    final JTextField txtText1 = new JTextField();
    NavigationFilter filter = new NavigationFilter() {
        @Override
        public void setDot(FilterBypass fb, int dot, Bias bias) {
            if (dot >= maxSize) {
                fb.setDot(0, bias);
                txtText1.transferFocus();
                return;
            }
            fb.setDot(dot, bias);
        }

        @Override
        public void moveDot(FilterBypass fb, int dot, Bias bias) {
            if (dot >= maxSize) { 
                fb.setDot(0, bias);
                txtText1.transferFocus();
                return;
            }
            fb.moveDot(dot, bias);
        }
    };      
    txtText1.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent arg0) {

                if (txtText1.getText().equals("")) {
                    txtDate.setText("");
                } else {
                    SwingWorker<?, ?> job = new UIQuery();
                job.execute();
                }
         }
    });         
    txtText1.setNavigationFilter(filter);
    ((AbstractDocument) txtText1.getDocument()).setDocumentFilter(new DocumentSizeFilter(maxSize));   
    panel.add(txtText1, "cell 1 1,growx");
    txtText1.setColumns(10);
}   
JLabel lblText2 = new JLabel("Production Date");
panel.add(lblText2, "cell 0 2,alignx trailing");
txtText2 = new JTextField();
panel.add(txtText2, "flowx,cell 1 2,growx");
txtText2.setColumns(10);
txtText2.addFocusListener(new TextBoxGainedFocusEventSinglePreview());
txtText2.getDocument().addDocumentListener(new TextBoxDataChangedEventSinglePreview());

Please advise how should I modify it. Thanks

Upvotes: 0

Views: 243

Answers (2)

camickr
camickr

Reputation: 324147

Don't know what the better design is:

  1. Have two classes, one to limit the characters and the other to do the tabbing, or
  2. Have one class to limit the characters and do the tabbing

You have a solution for the first approach.

For the second approach you can check out:

import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.AbstractDocument;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.DocumentFilter.FilterBypass;
import javax.swing.text.JTextComponent;

/**
 *  A DocumentFilter that allows you to control the maximum number of
 *  characters that can be added to the Document. When the Document is
 *  full you can optionally tab to the next component to speed data entry.
 *
 *  This class can also be used as a generic size filter for JTextFields. In
 *  this case when a size of 0 is speicifed for the size of the Document the
 *  getColumns() method of JTextField will be used to determine the size
 *  restriction.
 */
public class TabDocumentFilter extends DocumentFilter
{
    private int size;
    private boolean autoTab = true;

    /**
     *  Generic constructor for use with JTextFields only. The size of the
     *  Document will be determined by the value of the getColumns() method.
     */
    public TabDocumentFilter()
    {
        this(0);
    }

    /**
     *  Constructor to set the size for this filter
     *
     *  @param size maximum number of characters to be added to the Document
     */
    public TabDocumentFilter(int size)
    {
        setSize( size );
    }

    /**
     *  Get the auto tab property
     *
     *  @return the auto tab property
     */
    public boolean getAutoTab()
    {
        return autoTab;
    }

    /**
     *  Set the auto tab property
     *
     *  @param autoTab the default is true
     */
    public void setAutoTab(boolean autoTab)
    {
        this.autoTab = autoTab;
    }

    /**
     *  Get the maximum size for any Document using this filter
     *
     *  @return
     */
    public int getSize()
    {
        return size;
    }

    /**
     *  Set maximum size for a Document using this filter. Dynamically changing
     *  the size will not affect existing Documents. Characters will not be
     *  removed from any Document. The filter will only be invoked on new
     *  additions to the Document.
     *
     *  @param size the maximum number of character allowed in the Document
     */
    public void setSize(int size)
    {
        this.size = size;
    }

    /**
     *  Install this filter on the AbstractDocument
     *
     *  @param components the text components that will use this filter
     */
    public void installFilter(JTextComponent... components)
    {
        for (JTextComponent component : components)
        {
            Document doc = component.getDocument();

            if (doc instanceof AbstractDocument)
            {
                ((AbstractDocument)doc).setDocumentFilter( this );
            }
        }
    }

    /**
     *  Make sure the insertion of text will not cause the Document to exceed
     *  its size limit. Also, potentially tab to next component when full.
     */
    @Override
    public void insertString(FilterBypass fb, int offs, String str, AttributeSet a)
        throws BadLocationException
    {
        int possibleSize = fb.getDocument().getLength() + str.length();
        int allowedSize = getAllowedSize( fb );

        if (possibleSize <= allowedSize)
        {
            super.insertString(fb, offs, str, a);
            handleAutoTab(possibleSize, allowedSize, fb);
        }
        else
        {
            Toolkit.getDefaultToolkit().beep();
        }
    }

    /**
     *  Make sure the replacement of text will not cause the Document to exceed
     *  its size limit. Also, potentially tab to next component when full.
     */
    @Override
    public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a)
        throws BadLocationException
    {
        int possibleSize = fb.getDocument().getLength() + str.length() - length;
        int allowedSize = getAllowedSize( fb );

        if (possibleSize <= allowedSize)
        {
            super.replace(fb, offs, length, str, a);
            handleAutoTab(possibleSize, allowedSize, fb);
        }
        else
        {
            Toolkit.getDefaultToolkit().beep();
        }
    }

    /**
     *  When a size isn't specified then we assume the desired size can be
     *  obtained from the associated text field. Otherwise, use the class
     *  size property.
     */
    private int getAllowedSize(FilterBypass fb)
    {
        return (size == 0) ? getColumns(fb) : size;
    }

    /*
     *  Use the value returnd by invoking the getColumns() method of JTextField
     */
    private int getColumns(FilterBypass fb)
    {
        //  Find the text field that currently has focus
        //  and make sure it is using the Document that will be updated

        Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

        if (c != null && c instanceof JTextField)
        {
            JTextField textField = (JTextField)c;
            Document doc = textField.getDocument();

            if (doc.equals( fb.getDocument() ))
            {
                return textField.getColumns();
            }
        }

        return 0;
    }

    /*
     *  When the Document is full tab to the next component.
     */
    protected void handleAutoTab(int possibleSize, int allowedSize, FilterBypass fb)
    {
        if (autoTab == false
        ||  possibleSize != allowedSize)
            return;

        //  Find the text field that currently has focus
        //  and make sure it is using the Document that has been updated

        Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

        if (c != null && c instanceof JTextComponent)
        {
            JTextComponent component = (JTextComponent)c;
            Document doc = component.getDocument();

            if (doc.equals( fb.getDocument() ))
            {
                c.transferFocus();
            }
        }
    }
}

To use it you can do something like:

TabDocumentFilter tf = new TabDocumentFilter();
tf.installFilter( textField1, textField2 );

The characters of each text field will be limited based on the number of columns specified for the text field and auto tabbing will occur.

The above solution was based on the solution proposed in Text Field Auto Tab, which provided an approach for nesting DocumentFilters.

Upvotes: 1

MadProgrammer
MadProgrammer

Reputation: 347314

So, this is a simple proof of concept, which will move focus from the current component to the next when than field's Document length > 6

import java.awt.KeyboardFocusManager;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

public class Test {

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

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Test");
                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() {
            JTextField field1 = new JTextField(7);
            JTextField field2 = new JTextField(7);

            add(field1);
            add(field2);

            field1.getDocument().addDocumentListener(new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent e) {
                    if (e.getDocument().getLength() > 6) {
                        //field1.transferFocus();
                        KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
                    }
                }

                @Override
                public void removeUpdate(DocumentEvent e) {
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    if (e.getDocument().getLength() > 6) {
                        //field1.transferFocus();
                        KeyboardFocusManager.getCurrentKeyboardFocusManager().focusNextComponent();
                    }
                }
            });
        }

    }
}

When something doesn't work, start by removing everything that's not needed to test it (ie the DocumentFilter and NavigationFilter), get it working in as an isolated state as you can, once you know it's working, re-introduce the other elements one at a time and ensure nothing breaks.

It's possible that the DocumentSizeFilter might be interfering with it, but I don't have enough context to be 100% sure

Upvotes: 2

Related Questions