JavaFX中替代Swing javax.swing.text.Document的方案咨询
在JavaFX中替代Swing的ConstrainedDocument实现
核心思路:JavaFX文本处理机制
JavaFX没有直接对应Swing javax.swing.text.Document的类,它的文本组件(如TextField、TextArea)通过TextInputControl的textProperty()绑定内容。实现类似限制有两种主流方式:
- TextFormatter:这是JavaFX官方推荐的方案,通过
TextFormatter.Change对象拦截文本变更,在生效前完成校验与修改,效率更高且无界面闪烁。 - 监听
textProperty():属于变更后处理逻辑,易出现界面闪烁,不推荐使用。
封装所有限制需求的实现类
下面是对应原ConstrainedDocument功能的ConstrainedTextFormatter类,覆盖行长度限制、行数限制、强制大写、允许字符限制四种需求:
import javafx.scene.control.TextFormatter; import javafx.util.StringConverter; public class ConstrainedTextFormatter extends TextFormatter<String> { private boolean limitLineLength = false; private int maxLineLength = -1; private boolean limitLines = false; private int maxLines = -1; private boolean restrictCharacters = false; private String allowedCharacters = null; private boolean forceUppercase = false; public ConstrainedTextFormatter() { super(new StringConverter<>() { @Override public String toString(String object) { return object; } @Override public String fromString(String string) { return string; } }, "", this::filterChange); } private TextFormatter.Change filterChange(TextFormatter.Change change) { if (change.isDeleted()) { return change; } String newText = change.getControlNewText(); StringBuilder filteredText = new StringBuilder(); // 处理强制大写与字符限制 for (char c : change.getText().toCharArray()) { char processedChar = forceUppercase ? Character.toUpperCase(c) : c; if (!restrictCharacters || allowedCharacters.indexOf(processedChar) != -1) { filteredText.append(processedChar); } } change.setText(filteredText.toString()); if (filteredText.length() == 0) { return null; } // 处理行数与行长度限制 String[] lines = newText.split("\n", -1); int currentLineCount = lines.length; boolean exceedsLineLimit = limitLines && currentLineCount > maxLines; if (!exceedsLineLimit && limitLineLength) { boolean lineLengthExceeded = false; for (String line : lines) { if (line.length() > maxLineLength) { lineLengthExceeded = true; break; } } if (lineLengthExceeded) { String validText = buildValidText(newText); change.setText(validText.substring(change.getRangeStart(), change.getRangeEnd() + filteredText.length())); change.setRange(0, newText.length()); change.setControlText(validText); change.setCaretPosition(validText.length()); change.setAnchor(validText.length()); } } else if (exceedsLineLimit) { StringBuilder validLines = new StringBuilder(); for (int i = 0; i < maxLines; i++) { validLines.append(lines[i]); if (i < maxLines - 1) { validLines.append("\n"); } } change.setControlText(validLines.toString()); change.setRange(0, newText.length()); change.setText(""); change.setCaretPosition(validLines.length()); change.setAnchor(validLines.length()); } return change; } private String buildValidText(String text) { StringBuilder validText = new StringBuilder(); String[] lines = text.split("\n", -1); int lineCount = 0; for (String line : lines) { if (limitLines && lineCount >= maxLines) { break; } if (line.length() > maxLineLength) { validText.append(line.substring(0, maxLineLength)); } else { validText.append(line); } lineCount++; if (lineCount < lines.length && (!limitLines || lineCount < maxLines)) { validText.append("\n"); } } return validText.toString(); } // 对应原类的Setter与Getter方法 public void setMaximumLineLength(int maxLength) { this.maxLineLength = maxLength; this.limitLineLength = true; } public void setMaximumLines(int maxLines) { this.maxLines = maxLines; this.limitLines = true; } public void setAllowedCharacters(String allowedText) { this.allowedCharacters = allowedText; this.restrictCharacters = true; } public void setForceUppercase(boolean uppercase) { this.forceUppercase = uppercase; } public void resetLimitations() { this.restrictCharacters = false; this.limitLineLength = false; this.limitLines = false; this.forceUppercase = false; } public int getMaximumLineLength() { return maxLineLength; } public int getMaximumLines() { return maxLines; } public boolean isLineLengthLimited() { return limitLineLength; } public boolean isLinesLimited() { return limitLines; } public String getAllowedCharacters() { return allowedCharacters; } public boolean isCharactersRestricted() { return restrictCharacters; } public boolean isForceUppercase() { return forceUppercase; } }
使用示例
将该TextFormatter应用到JavaFX文本组件,以TextArea为例:
TextArea textArea = new TextArea(); ConstrainedTextFormatter formatter = new ConstrainedTextFormatter(); formatter.setMaximumLineLength(20); // 每行最多20个字符 formatter.setMaximumLines(5); // 最多5行 formatter.setAllowedCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); // 仅允许大写字母与数字 formatter.setForceUppercase(true); // 强制输入内容为大写 textArea.setTextFormatter(formatter);
关键差异说明
- 文本模型差异:Swing通过
Document抽象层处理文本,JavaFX直接通过TextFormatter拦截变更,逻辑更轻量化。 - 处理时机差异:
TextFormatter在文本变更生效前处理,避免了Swing中先变更再回滚的问题。 - API设计差异:JavaFX的
TextFormatter.Change包含变更范围、新文本、光标位置等全量信息,可更精准控制文本修改。
内容的提问来源于stack exchange,提问作者Hervé Girod
相关产品推荐
相关产品推荐

