Java JFormattedTextField插入符号在焦点上的位置

Java JFormattedTextField插入符号在焦点上的位置,java,swing,focus,caret,jformattedtextfield,Java,Swing,Focus,Caret,Jformattedtextfield,我在程序中使用了一些JFormattedTextFields。出于某种原因,当文本字段在单击文本字段后获得焦点时,插入符号位置总是跳到左侧(位置0)。我希望插入符号结束于用户单击的位置。因此,如果我在两个数字之间单击,插入符号应该在这两个数字之间结束 因此,我实现了一个FocusListener,它将获取单击位置并在那里设置插入符号位置 FocusListener focusListener = new FocusListener(){ public void focusGained

我在程序中使用了一些JFormattedTextFields。出于某种原因,当文本字段在单击文本字段后获得焦点时,插入符号位置总是跳到左侧(位置0)。我希望插入符号结束于用户单击的位置。因此,如果我在两个数字之间单击,插入符号应该在这两个数字之间结束

因此,我实现了一个FocusListener,它将获取单击位置并在那里设置插入符号位置

FocusListener focusListener = new FocusListener(){


    public void focusGained(FocusEvent evt) {

        JFormettedTextField jftf = (JFormattedTextField) evt.getSource();

        //This is where the caret needs to be.
        int dot = jftf.getCaret().getDot(); 

        SwingUtilities.invokeLater( new Runnable() {

        public void run() {
'the textField that has focus'.setCaretPosition('Some how get the evt or dot');              
              }
           });
        }

    public void focusLost (FocusEvent evt) {}

    });
我试过很多方法让他工作。我试着使用final关键字,这很有效,但只适用于单个文本字段

我在focus listener中使用了set/get方法来分配当前对象,但不确定如何使其“安全”(例如,它们是否需要同步?)


也许我遗漏了什么?

你需要使用鼠标听筒:

MouseListener ml = new MouseAdapter()
{
    public void mousePressed(final MouseEvent e)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                JTextField tf = (JTextField)e.getSource();
                int offset = tf.viewToModel(e.getPoint());
                tf.setCaretPosition(offset);
            }
        });
    }
};

formattedTextField.addMouseListener(ml);

这实际上发生在
AbstractFormatter.install(JFormattedTextField)
中,该字段在获得焦点时被调用

我不知道为什么它是这样设计的,但您可以重写这种行为(只要格式化程序不更改字段中字符串的长度)

示例(假设字段值为
int
):


请注意,这与默认的
Integer
格式化程序不同。默认格式设置程序使用分隔数字组的
十进制格式,例如
“1000000”
。这使得任务更加困难,因为它改变了字符串的长度。

我认为finnw的解决方案有点改进。例如:

public static void main(String[] args) {
    NumberFormat format = NumberFormat.getInstance();
    NumberFormatter formatter = new NumberFormatter(format) {
        @Override
        public void install(JFormattedTextField pField) {
            final JFormattedTextField oldField = getFormattedTextField();
            final int oldLength = pField.getDocument().getLength();
            final int oldPosition = pField.getCaretPosition();

            super.install(pField);

            if (oldField == pField && oldLength == pField.getDocument().getLength()) {
                pField.setCaretPosition(oldPosition);
            }
        }
    };
    JFormattedTextField field = new JFormattedTextField(formatter);
    field.setValue(1234567890);

    JOptionPane.showMessageDialog(null, field);
}

如果无法或不希望覆盖格式化程序:

    import java.awt.event.FocusEvent;
    import java.awt.event.FocusListener;
    import java.text.NumberFormat;
    
    import javax.swing.JFormattedTextField;
    import javax.swing.JOptionPane;
    import javax.swing.SwingUtilities;
    import javax.swing.text.NumberFormatter;
    
    public class TestJFormattedTextFieldFocus {
    
        public static void main(String[] args) {
            NumberFormat format = NumberFormat.getInstance();
            NumberFormatter formatter = new NumberFormatter(format);
            JFormattedTextField field = new JFormattedTextField(formatter);
            field.addFocusListener(new FocusListener() {
    
                @Override
                public void focusLost(FocusEvent e) {
                }
    
                @Override
                public void focusGained(FocusEvent e) {
                    restoreCaretPosition();
                }
    
                private void restoreCaretPosition() {
                    int caretPosition = field.getCaretPosition();
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            field.setCaretPosition(caretPosition);
                        }
                    });
                }
            });
            field.setValue(1234567890);
            JOptionPane.showMessageDialog(null, field);
        }
    
    }

回答得好!但为什么需要在invokeLater()中执行此操作?mousePressed()不是从事件线程调用的吗?@Sanoj,
invokeLater
引入的延迟是它工作所必需的。通常,单击字段时会获得焦点,这会导致格式化程序重新格式化值并更新字段文本。这样做的一个副作用是插入符号被移动。使用
invokeLater
,此
run()
方法在焦点事件处理完成之前不会执行,因此,您知道,一旦您将插入符号放在正确的位置,它就会留在那里。如果
TableCellEditor中使用
JFormattedTextField
,则此解决方案不起作用,但finnw的解决方案会起作用。@Jonas事实上,它起作用(假设您的评论与您最近的问题相关)-您必须重新发送转换的(到文本字段的坐标)MouseEvent或手动执行我答案中的//do stuff部分中的设置:-)@kleopatra:没有你的“hack”就不行,而finnw的解决方案在没有修改的情况下效果很好。
    import java.awt.event.FocusEvent;
    import java.awt.event.FocusListener;
    import java.text.NumberFormat;
    
    import javax.swing.JFormattedTextField;
    import javax.swing.JOptionPane;
    import javax.swing.SwingUtilities;
    import javax.swing.text.NumberFormatter;
    
    public class TestJFormattedTextFieldFocus {
    
        public static void main(String[] args) {
            NumberFormat format = NumberFormat.getInstance();
            NumberFormatter formatter = new NumberFormatter(format);
            JFormattedTextField field = new JFormattedTextField(formatter);
            field.addFocusListener(new FocusListener() {
    
                @Override
                public void focusLost(FocusEvent e) {
                }
    
                @Override
                public void focusGained(FocusEvent e) {
                    restoreCaretPosition();
                }
    
                private void restoreCaretPosition() {
                    int caretPosition = field.getCaretPosition();
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            field.setCaretPosition(caretPosition);
                        }
                    });
                }
            });
            field.setValue(1234567890);
            JOptionPane.showMessageDialog(null, field);
        }
    
    }