You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键差异说明

  1. 文本模型差异:Swing通过Document抽象层处理文本,JavaFX直接通过TextFormatter拦截变更,逻辑更轻量化。
  2. 处理时机差异:TextFormatter在文本变更生效前处理,避免了Swing中先变更再回滚的问题。
  3. API设计差异:JavaFX的TextFormatter.Change包含变更范围、新文本、光标位置等全量信息,可更精准控制文本修改。

内容的提问来源于stack exchange,提问作者Hervé Girod

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:09:57