如何进行TextArea拉伸以填充内容,在过程中扩展父级?

时间:2013-09-03 09:32:06

标签: javafx-2 javafx-8

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

我试过

myTextArea.autoSize()

包裹在

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

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

左,右&顶部锚点设置为它的父级AnchorPane。我尝试了底部连接而没有连接。理想情况下,我希望随着textarea的增长而增长锚定窗格。

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

更新

所以我想也许我正在过度思考它,而我所需要做的就是关掉滚动条,剩下的就会发生。唉,寻找“滚动”,“垂直”,“vbar”的可用字段和方法,我找不到任何可以使用的东西。 ScrollTopProperty看起来像是其他东西。

4 个答案:

答案 0 :(得分:12)

问题;当文本通过用户的输入或复制粘贴而改变时,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);
    }
}

但它有一个缺点; textarea的高度只有在多行之间有换行符(即输入键)时才会改变,如果用户键入的时间足够长,则文本被包裹到多行,但高度不会改变。

为了解决这个问题,我添加了这一行

textHolder.setWrappingWidth(textArea.getWidth() - 10);
primaryStage.show();之后

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

在这两种情况下,复制粘贴都处理得当。

答案 1 :(得分:7)

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

  • 查找textarea的垂直滚动条。
  • 让它透明
  • 听取其可见属性
  • 当滚动条变为可见时,我向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;
    }
}

答案 2 :(得分: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);
    };
  }

答案 3 :(得分:0)

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

这种方法的问题在于,需要重新绘制窗口几次,直到找到合适的高度为止。

new Stage {
  title = plotTitle
  scene = new Scene(600, 600) {
    val series = new XYChart.Series[Number, Number]()
    series.setName(s"Simple")

    // Specify an X-axis with fixed upper & lower bounds, and tick unit
    // (separation of major tick values).
    //
    // Note: This is automatically not an auto-ranging axis.
    val xAxis = NumberAxis("Time (s)", 115.0, 225.0, 25.0)
    val yAxis = NumberAxis("Amplitude")
    val lineChart = new LineChart(xAxis, yAxis)
    lineChart.setAnimated(false)
    lineChart.getData.add(series)
    lineChart.setCreateSymbols(false)
    root = lineChart

    var index = 0
    val timer = AnimationTimer { time =>
      series.getData.add(XYChart.Data[Number, Number](index, math.cos(index)))
      if (series.getData.size > 100) series.getData.remove(0, series.getData.size() - 100)
      index += 1
    }
    timer.start()
  }
}