
在使用 javax.swing.jformattedtextfield 进行数值输入时,我们通常会结合 javax.swing.text.numberformatter 来实现自定义的格式化规则,例如添加货币前缀、限制输入范围等。然而,当自定义格式化器在用户输入第一个数字时自动添加前缀(如 "gs. ")后,可能会出现光标位置不正确的问题,即光标跳到前缀之前,而不是用户输入的数字之后。
最初的尝试可能是在 NumberFormatter 的 install 方法中设置光标位置:
@Override
public void install(JFormattedTextField pField) {
super.install(pField);
pField.setCaretPosition(pField.getDocument().getLength()); // 尝试设置光标到末尾
}然而,这种方法并不能解决动态输入时光标错位的问题。原因在于,install 方法仅在 JFormattedTextField 对象被创建并关联格式化器时调用一次,而非在每次 JFormattedTextField 获取焦点或文本内容变化时调用。根据 Javadoc,install 方法主要用于子类安装额外的监听器。因此,我们需要一种机制来响应文本内容的实时变化。
为了在 JFormattedTextField 的文本内容发生变化时动态调整光标位置,最合适的方案是使用 javax.swing.event.DocumentListener。DocumentListener 允许我们监听 Document 模型的插入、删除和属性更改事件。
我们将 DocumentListener 添加到 JFormattedTextField 的 Document 对象上。当文本内容发生变化(insertUpdate 或 removeUpdate)时,我们就可以重新设置光标位置。
在 DocumentListener 的回调方法中直接调用 pField.setCaretPosition() 可能会导致与 Swing 内部其他 DocumentListener 的执行顺序冲突。Swing 框架本身会设置一些 DocumentListener 来处理文本格式化和验证。为了确保我们的光标设置操作在所有相关的 Swing 内部处理完成后执行,我们应该将 setCaretPosition 调用封装在 EventQueue.invokeLater() 中。invokeLater 会将任务放入 Swing 事件调度线程的末尾,从而保证其在当前事件处理周期结束后执行。
以下是修改后的 formato() 方法,其中包含了正确处理光标定位的 DocumentListener 实现,以及其他自定义的格式化逻辑。
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.NumberFormatter;
import java.awt.*;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;
public class JFormattedTextFieldCaretFix {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("JFormattedTextField 光标定位示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
JFormattedTextField textFieldMonto = new JFormattedTextField(formato());
textFieldMonto.setPreferredSize(new Dimension(200, 30));
frame.add(textFieldMonto);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
private static NumberFormatter formato() {
// 定义货币格式,例如 "Gs. 1,234"
DecimalFormat myFormatter = new DecimalFormat("'Gs. '###,##0;'Gs. '###,##0");
NumberFormatter numberFormatter = new NumberFormatter(myFormatter) {
// 重写 install 方法,添加 DocumentListener 来动态管理光标位置
@Override
public void install(JFormattedTextField pField) {
super.install(pField);
pField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
// 在文本插入后,将光标设置到文档末尾
EventQueue.invokeLater(() -> pField.setCaretPosition(pField.getDocument().getLength()));
}
@Override
public void removeUpdate(DocumentEvent e) {
// 在文本删除后,将光标设置到文档末尾
EventQueue.invokeLater(() -> pField.setCaretPosition(pField.getDocument().getLength()));
}
@Override
public void changedUpdate(DocumentEvent e) {
// 属性更改(如字体颜色)不影响光标位置,无需处理
}
});
}
// 允许空文本,并阻止负数
@Override
public String valueToString(Object value) throws ParseException {
String result = super.valueToString(value);
// 如果格式化结果以 "-" 开头,则移除它,阻止负数显示
if (result != null && result.startsWith("-")) {
result = result.replaceFirst("-", "");
}
// 如果值为 null,返回空字符串,允许 JFormattedTextField 为空
if (value == null) {
return "";
}
return result;
}
// 允许空文本,并阻止负数
@Override
public Object stringToValue(String text) throws ParseException {
// 如果文本为空或只包含前缀,则返回 null,表示空值
if (text.length() == 0 || text.equals("Gs. ")) {
return null;
}
// 移除文本中的负号,阻止负数输入
text = text.replaceFirst("-", "");
// 如果文本不以 "Gs. " 开头(用户直接输入数字),则自动添加前缀
if (!text.startsWith("Gs. ")) {
text = "Gs. " + text;
}
return super.stringToValue(text);
}
};
// 关键属性设置
numberFormatter.setAllowsInvalid(false); // 不允许输入无效字符
numberFormatter.setMaximum(new BigDecimal("999999999999")); // 设置最大允许值
numberFormatter.setCommitsOnValidEdit(true); // 每次有效编辑后立即提交值,而不是失去焦点时
return numberFormatter;
}
}通过在 NumberFormatter 的 install 方法中巧妙地集成 DocumentListener,并结合 EventQueue.invokeLater 来异步更新光标位置,我们能够有效解决 JFormattedTextField 在自定义格式化(特别是涉及前缀添加)时光标定位不准确的问题。这种方法确保了光标始终位于用户期望的位置,极大地提升了用户输入体验,同时保持了 JFormattedTextField 强大的格式化能力。在开发复杂的 Swing 表单时,理解并正确应用这些机制是至关重要的。
以上就是JFormattedTextField 自定义格式化器中光标位置问题的解决方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号