如何在 JTextField 获得焦点时将光标定位到末尾位置是最佳方式?

6

默认情况下,当JTextField获得焦点时,插入符号会定位在文本的开头。然而,我认为更好的行为是将其定位在末尾,或者选择所有文本,例如http://jsfiddle.net/Marcel/jvJzX/。有什么好的方法可以实现这一点?理想情况下,解决方案应该全局适用于应用程序中的所有JTextFields。

默认行为示例(按Tab键聚焦到字段):

public static void main(String[] args) {
    JTextField field = new JTextField("hello world!");
    JOptionPane.showMessageDialog(null, field);
}

编辑: 澄清一下,如果我不必搜寻我的应用并更改所有文本字段,那就太好了。


将以下与编程相关的内容从英语翻译为中文。仅返回已翻译的文本: - Peter Tseng
这可能是一个重要的区别(我的看法),有两种方法将Caret移动到文档末尾,可以参考@MadProgrammer的答案或者使用selectAll()方法,可以查看我在@MadProgrammer的帖子中的评论。 - mKorbel
3个回答

9

实际行为和要求都没有完全描述:

当 JTextField 获得焦点时,插入符号将定位于文本开头

这并不完全正确:当通过以下方式获得焦点时:

  • 点击,插入符号将放置在鼠标位置
  • 其他方式(使用 Tab 键或编程方式),插入符号将被放置在失去焦点时的位置。

因此,下面是要求:

更好的行为是将其定位在末尾,或选择所有文本

对于那些不想降低可用性的情况需要一些思考,至少对于第一批用户而言,如果鼠标手势被覆盖了,他们可能会感到困惑。第二个要求可以讨论,可能与操作系统/外观和感觉有关。就个人而言,如果插入符号不在开头,我不会触及它。

从技术上讲,一种全局触发组件状态更改的解决方案是在 KeyboardFocusManager 中注册 PropertyChangeListener:

PropertyChangeListener pl = new PropertyChangeListener() {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (!(evt.getNewValue() instanceof JTextField)) return;
        JTextField field = (JTextField) evt.getNewValue();
        // crude check to not overdo it
        int dot = field.getCaretPosition();
        if (dot == 0) {
            field.selectAll();
        }
    }
};
KeyboardFocusManager.getCurrentKeyboardFocusManager()
    .addPropertyChangeListener("permanentFocusOwner", pl);

3
全球通用解决方案加一分。selectAll() 应该包裹在 SwingUtilities.invokeLater() 中,否则在 JFormattedTextField 上的选择功能将无法工作。 - camickr
@camickr 很好的观点!虽然我不太确定格式化文本字段的可用性 - 它的navigationFilter可能有自己的想法? - kleopatra
1
+1 - 你可能还想检查一下 event.getOldValue() 是否为 JTextField,以便撤销 selectAll() - splungebob
@splungebob 很好的思考点!但可能取决于具体要求 - 不确定是否总是希望取消选择,即使在旧字段中全部选择了? - kleopatra

4

向字段添加FocusListener

当触发focusGained时,将字段的插入符位置设置为文本末尾...

field.setCaretPosition(field.getDocument().getLength());

请参考如何编写焦点监听器以获取更多详细信息。

更新

要选择所有文本,可以使用...

field.selectAll();

这将把光标移动到末尾。

我过去做的是创建一个实用程序类(例如AutoSelectOnFocusManager),其中有一个单一的FocusListener。基本上,您可以向其注册(或取消注册)JTextComponent并由它为您管理该过程。省了很多重复的代码: P

更新为简单示例

为了测试评论中的反馈而做了这个简单的例子,我想我也会放进去...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Wackme {

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

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

                JTextField field1 = new JTextField("Some text", 20);
                JTextField field2 = new JTextField("Some text", 20);

                field1.addFocusListener(new FocusAdapter() {
                    @Override
                    public void focusGained(FocusEvent e) {
                        System.out.println("Move to end");
                        JTextField field = ((JTextField)e.getComponent());
                        field.selectAll();
                        //field.setCaretPosition(field.getDocument().getLength());
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new GridBagLayout());
                frame.add(field1);
                frame.add(field2);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}

我认为应该是 field.getDocument().getLength() - 1 - user330315
1
@MadProgrammer 我认为(http://stackoverflow.com/a/8544512/714968),但是XXX.selectAll()需要被包装在`invokeLater`中。 - mKorbel
1
@mKorbel 这取决于情况,如果是 JSpinner,那肯定会这样。 - MadProgrammer
我认为当一个JFormattedTextField被单独使用或作为另一个组件的编辑器时,需要使用invokeLater。 - camickr

-2

你需要创建你自己的JTextField,并覆盖必要的方法以实现你想要的功能,然后按照你的意愿执行步骤,比如在焦点获得时选择所有值,然后在该方法中编写你的代码


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接