18

所以我有一个 TextArea 并且当用户将段落粘贴到其中,或者只是在其中写入时,我希望它垂直扩展以显示所有可用的文本。即不要在文本字段本身中使用滚动条......就像在许多网页上发生的那样。许多用户,包括我自己在内,都不喜欢被迫在小窗口中进行编辑。Facebook 状态更新框的工作原理。

我试过了

myTextArea.autoSize()

包裹在一个

myTextArea.textProperty().addListener(new ChangeListener()....);

但这不起作用。我认为它很高兴自动调整到当前大小。

左侧、右侧和顶部锚点设置为其父 AnchorPane。我试过用底部连接而不是连接。理想情况下,我想随着文本区域的增长而增长锚窗格。

我不介意阅读 TextProperty 并计算我自己设置的触发器大小......但如果已经有最佳实践,这似乎是一种 hacky 方法。javafx 的属性和子对象的数量足以令人生畏,在这里提出这个问题似乎是一个好点,而不是试图弄清楚字体/段落等占用了多少像素。

更新:

所以我想也许我想多了,我需要做的就是关掉滚动条,剩下的事情就会发生。唉,为“scroll”、“vertical”、“vbar”寻找可用的字段和方法却没有我可以使用的东西。ScrollTopProperty 看起来像是用于其他用途。

4

5 回答 5

14

问题; textArea 的高度希望在其文本通过用户键入或复制粘贴而改变时增加或缩小。这是另一种方法:

public class TextAreaDemo extends Application {

    private Text textHolder = new Text();
    private double oldHeight = 0;

    @Override
    public void start(Stage primaryStage) {
        final TextArea textArea = new TextArea();
        textArea.setPrefSize(200, 40);
        textArea.setWrapText(true);

        textHolder.textProperty().bind(textArea.textProperty());
        textHolder.layoutBoundsProperty().addListener(new ChangeListener<Bounds>() {
            @Override
            public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
                if (oldHeight != newValue.getHeight()) {
                    System.out.println("newValue = " + newValue.getHeight());
                    oldHeight = newValue.getHeight();
                    textArea.setPrefHeight(textHolder.getLayoutBounds().getHeight() + 20); // +20 is for paddings
                }
            }
        });

        Group root = new Group(textArea);
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();

        //  See the explanation below of the following line. 
        //  textHolder.setWrappingWidth(textArea.getWidth() - 10);  // -10 for left-right padding. Exact value can be obtained from caspian.css
    }

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

但它有一个缺点;仅当多行之间存在换行符(即 Enter 键)时,textarea 的高度才会发生变化,如果用户键入的时间足够长,则文本会被换成多行但高度不会改变。

为了解决这个缺点,我添加了这一行

textHolder.setWrappingWidth(textArea.getWidth() - 10);

之后primaryStage.show();。它适用于用户不换行的长打字。然而,这产生了另一个问题。当用户通过点击“退格”删除文本时,会出现此问题。当 textHolder 的高度发生变化并且 textArea 的高度设置为新值时,就会出现问题。IMO 它可能是一个错误,没有更深入地观察。

在这两种情况下,复制粘贴都可以正确处理。

于 2013-10-04T20:26:29.077 回答
8

等待更好的,我使用这个 hacky 解决方案。

  • 查找文本区域的垂直滚动条。
  • 让它透明
  • 听它的可见属性
  • 当滚动条变得可见时,我在 textarea 中添加一行。

编码:

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class GrowGrowTextArea extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        AnchorPane root = new AnchorPane();
        root.setStyle("-fx-padding:20;-fx-background-color:dodgerblue;");
        final TextArea textArea = new TextArea();
        AnchorPane.setTopAnchor(textArea, 10.0);
        AnchorPane.setLeftAnchor(textArea, 10.0);
        AnchorPane.setRightAnchor(textArea, 10.0);
        root.getChildren().add(textArea);
        primaryStage.setScene(new Scene(root, 400, 300));
        primaryStage.show();
        ScrollBar scrollBar = lookupVerticalScrollBar(textArea);
        scrollBar.setOpacity(0.0);
        scrollBar.visibleProperty().addListener(new ChangeListener<Boolean>() {

            @Override
            public void changed(ObservableValue<? extends Boolean> source,
                                Boolean wasVisible,
                                Boolean isVisible) {
                if (isVisible) {
                    textArea.setPrefRowCount(textArea.getPrefRowCount() + 1);
                    textArea.requestLayout();
                }
            }
        });

    }

    private ScrollBar lookupVerticalScrollBar(Node node) {
        if (node instanceof ScrollBar && ((ScrollBar)node).getOrientation() == Orientation.VERTICAL) {
            return (ScrollBar) node;
        }
        if (node instanceof Parent) {
            ObservableList<Node> children = ((Parent) node).getChildrenUnmodifiable();
            for (Node child : children) {
                ScrollBar scrollBar = lookupVerticalScrollBar(child);
                if (scrollBar != null) {
                    return scrollBar;
                }
            }
        }
        return null;
    }
}
于 2013-09-30T07:58:13.180 回答
2

我在创建扩展 TextArea 时遇到了类似的问题。我正在创建看起来像 TextField 的 TextArea 并在每次没有更多空间时垂直扩展。我已经在堆栈和其他可用资源上测试了我能找到的关于这个主题的所有解决方案。我发现很少有好的解决方案,但都不够好。

经过几个小时的战斗,我想出了这种方法。

我扩展了 TextArea 类,覆盖 layoutChildren() 方法并在文本高度上添加了一个侦听器。

  @Override
  protected void layoutChildren() {
    super.layoutChildren();
    setWrapText(true);
    addListenerToTextHeight();
  }

  private void addListenerToTextHeight() {
    ScrollPane scrollPane = (ScrollPane) lookup(".scroll-pane");
    scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
    scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);

    StackPane viewport = (StackPane) scrollPane.lookup(".viewport");

    Region content = (Region) viewport.lookup(".content");

    Text text = (Text) content.lookup(".text");
    text.textProperty().addListener(textHeightListener(text));
  }

  private InvalidationListener textHeightListener(Text text) {
    return (property) -> {
      // + 1 for little margin
      double textHeight = text.getBoundsInLocal().getHeight() + 1;

      //To prevent that our TextArena will be smaller than our TextField
      //I used DEFAULT_HEIGHT = 18.0
      if (textHeight < DEFAULT_HEIGHT) {
        textHeight = DEFAULT_HEIGHT;
      }

      setMinHeight(textHeight);
      setPrefHeight(textHeight);
      setMaxHeight(textHeight);
    };
  }
于 2017-10-05T16:26:56.847 回答
0

我使用了之前答案中的一些代码。growTextAreaIfNecessary 方法会增加 textArea 的高度,直到滚动条不可见(本例中限制为 20 行)。

这种方法的问题是窗口需要重绘几次,直到找到完美的高度。

private ScrollBar lookupVerticalScrollBar(Node node) {
    if (node instanceof ScrollBar && ((ScrollBar) node).getOrientation() == Orientation.VERTICAL) {
        return (ScrollBar) node;
    }
    if (node instanceof Parent) {
        ObservableList<Node> children = ((Parent) node).getChildrenUnmodifiable();
        for (Node child : children) {
            ScrollBar scrollBar = lookupVerticalScrollBar(child);
            if (scrollBar != null) {
                return scrollBar;
            }
        }
    }
    return null;
}

private void growTextAreaIfNecessary(TextArea textArea) {
    Platform.runLater(() -> {
        ScrollBar lookupVerticalScrollBar = lookupVerticalScrollBar(textArea);
        int prefRowCount = textArea.getPrefRowCount();

        if (lookupVerticalScrollBar.isVisible() && prefRowCount < 20) {
            textArea.setPrefRowCount(prefRowCount + 1);
            System.out.println("increasing height to: " + (prefRowCount + 1));
            growTextAreaIfNecessary(textArea);
        }
    });
}
于 2020-05-12T17:17:58.377 回答
0

我尝试了很多技巧,其中大多数在打字时都有抖动,这对我来说是完美的结果:

textArea.textProperty().addListener((obs,old,niu)->{
            Text t = new Text(old+niu);
            t.setFont(textArea.getFont());
            StackPane pane = new StackPane(t);
            pane.layout();
            double height = t.getLayoutBounds().getHeight();
            double padding = 20 ;
            textArea.setMinHeight(height+padding);
        });
于 2022-01-28T17:15:42.600 回答