Java : 光标自动从一个 TextField 移动到另一个 TextField 时出错

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

当我在TextField中输入第7个字符时,光标自动移动到下一个TextField。但是在我的代码中,我的光标没有转到 nextField。 (我知道我不能使用 addKeyListener,所以尝试为这段代码使用 DocumentListener)

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

请指教我该如何修改。 谢谢

所以,这是一个简单的概念证明,当字段的 Document 长度 > 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();
                    }
                }
            });
        }

    }
}

当某些东西不起作用时,首先删除测试它不需要的所有东西(即 DocumentFilterNavigationFilter),尽可能让它在孤立状态下工作,一旦你知道它正在工作,一次重新引入其他元素并确保没有任何中断。

DocumentSizeFilter 可能会干扰它,但我没有足够的上下文来 100% 确定

不知道更好的设计是什么:

  1. 有两个 classes,一个限制字符,另一个做制表符,或者
  2. 有一个class来限制字符并进行制表

你有第一种方法的解决方案。

对于第二种方法,您可以查看:

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

要使用它,您可以执行以下操作:

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

每个文本字段的字符将根据为文本字段指定的列数进行限制,并且会出现自动跳格。

上述解决方案基于Text Field Auto Tab中提出的解决方案,提供了嵌套DocumentFilters的方法。