JavaFX - 限制TextArea的行数?

12

在Swing中,我创建了一个定制的Document,能够确定并限制TextArea中的行数,无论是被换行还是按下回车键。我希望在FX中找到类似的东西,但是还没有找到。有任何建议来限制允许的行数吗?

编辑:这就是我试图找出TextArea中有多少个换行符的方式。最大的问题是获取确切的宽度以传递给setWrappingWidth,因为TextArea的内容似乎带有一些内边距和边框。

Text helper = new Text();
helper.setText(text);
helper.setFont(getFont());
helper.wrappingWidthProperty().bind(widthBinding);
Font font = getFont();
FontMetrics fontMetrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(font);
int preferredHeight = new Double(helper.getLayoutBounds().getHeight()).intValue();
int lineHeight = new Double(fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent()).intValue();
System.err.println("preferredHeight / lineHeight: " + preferredHeight / lineHeight);
return preferredHeight / lineHeight;

.setPrefRowCount(10);”不起作用吗?即使“wrapText”设置为true?此外,文本区域会尊重其区域大小,因此它会尽力填充它,除非您使用maxsize或其他方法对其进行限制。 - Elltz
据我所知,setPrefRowCount和setPrefColCount仅影响TextArea的首选大小,而不限制内容。 - Tommo
这根本没有任何意义。你不能期望在缩小文本区域的同时限制行数。这是一个可用性噩梦。这有什么用例? - Roland
大小没有改变。假设TextArea定义为10行40列。首选大小已设置且不会更改,但在某些情况下,我将希望将文本限制为400个字符和/或10行。 Text节点被用作助手来确定当前首选大小,以便找出字段中当前有多少换行符(因为如果一行换行,则不存在换行符)。对于任何混淆,我离开了我的计算机并试图在手机上跟上。 - Tommo
所以,如果你有400个字符,其中前10个字符是换行符,你只想在文本区域缩小到1个字符列宽时保留这10个换行符吗?仍然没有限制行数而是限制字符数没有意义。 - Roland
我们正在将GUI从Swing重写为FX,这是一个很大的过程。目标是不必更改任何服务器端代码。我们以前在Swing中有许多TextAreas,例如3行40个字符或5行40个字符,它们不允许文本换行(因此需要换行符才能进入新行)。然后,服务器代码会在换行符上拆分文本以输入到3x40或5x40数据库字段中。当打开换行时,它使得更难确定字段中存在多少行并限制输入。此外,宽度/高度永远不会缩小。 - Tommo
3个回答

8
自Java 8更新40以来,每个TextInputControl都有一个TextFormatter来执行此任务。
它具有两个功能,其中之一可用于您的任务:

一个过滤器(getFilter()),可以拦截和修改用户输入。这有助于保持所需格式的文本。

这是解决此问题的非常原始的尝试:
public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        BorderPane root = new BorderPane();

        final TextArea textArea = new TextArea();
        textArea.setTextFormatter(createTextFormatter());
        root.setCenter(textArea);

        primaryStage.setScene(new Scene(root, 400, 400));
        primaryStage.show();
    }

    private static <T> TextFormatter<T> createTextFormatter() {

        final IntegerProperty lines = new SimpleIntegerProperty(1);

        return new TextFormatter<>(change -> {
            if (change.isAdded()) {
                if (change.getText().indexOf('\n') > -1) {
                    lines.set(lines.get() + 1);
                }
                if (lines.get() > 10) {
                    change.setText("");
                }
            }
            return change;
        });
    }

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

这个方法成功地防止用户输入超过10行。但你应该改进这个解决方案,例如处理以下情况:
  • 文字被替换
  • 文字被删除
TextFormatter非常强大,可以应对以上所有情况。

我会在几个小时内研究这个问题。我相信较难的部分是计算由于文本换行而产生的行数,而不是用户按下回车键。我现在离我的电脑有些远,但我曾经接近通过使用文本节点来解决这个问题。 - Tommo
@Tommo 但是现在你完全改变了问题的范围..从“如何(使用哪些方法、类文件)限制TextField中的行数”到“如何确定TextField中的行数”.. - eckig
它们有点相辅相成,不是吗?确定行数几乎是限制行数的必要条件。 - Tommo
那取决于几个因素。所以不一定是这样的。例如,我们从“行数”(这通常意味着由换行符分隔的字符串)转换为换行后的行数。 - eckig
问题的第一行并没有说明是换行还是按下了换行键。 - Tommo

0

简单的解决方案(未经优化,仅适用于文本不太长的情况):

  int maxRowCount = 3;
  new TextArea().setTextFormatter (new TextFormatter<> (change -> {
    if (change.isContentChange ()) {
      if (change.getControlNewText ().chars ().filter (a -> a == '\n').count () > maxRowCount - 1) {
        change.setText ("");
      }
    }
    return change;
  }));

0

通过以下方式分配TextFormatter:

setTextFormatter(new TextFormatter<>(change -> {
  if(change.isContentChange()) {
    if(getWrappedLines(change.getControlText(), change.getControlNewText()) > maxRows) {
      change.setText("");
    }
  }
  return change;
}));

还有getWrappedLines方法:

int getWrappedLines(String oldText, String newText) {
  Text helper = (Text)lookup(".text");
  if(helper != null) {
    helper.setText(newText);
    FontMetrics fontMetrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(getFont());
    int preferredHeight = new Double(helper.getLayoutBounds().getHeight()).intValue();
    int lineHeight = new Double(fontMetrics.getLineHeight()).intValue();
    // Initially a line's preferredHeight = 14, however each new line seems   to increase the preferredHeight by
    // 16 instead of 14.
    preferredHeight = preferredHeight == lineHeight ? preferredHeight : preferredHeight - 2;
    helper.setText(oldText);
    return preferredHeight / lineHeight;
  }
  else {
    return 0;
  }

}


1
请将以下与编程相关的内容从英文翻译成中文。只返回翻译后的文本:直接从此页面复制/粘贴,不要进行任何研究或提供解决方案。 - eckig
建议使用TextFormatter是朝着正确方向迈出的一步,然而getWrappedLines方法是我最初发布的。将两者结合起来解决了问题。 - Tommo
我完全同意。我只是想发布一下对我有效的解决方案,以防其他人遇到类似问题或有更好的建议解决方案。 - Tommo

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