Подтвердить что ты не робот

Как я могу сделать растяжку TextArea для заполнения контента, расширяя родительский процесс?

Итак, у меня есть TextArea, и когда пользователь вставляет в него абзацы или просто пишет в нем, я хочу, чтобы он расширялся вертикально, чтобы открыть весь доступный текст. То есть не использовать полосу прокрутки в самом текстовом поле... очень похоже на то, что происходит на многих веб-страницах. Многие пользователи, включая меня, не любят принудительно редактировать в маленьком окне. Точно, как работает окно обновления статуса Facebook.

Я пробовал

myTextArea.autoSize()

завернутый в

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

но это не сработает. Я думаю, что он доволен своим нынешним размером.

В левом, правом и верхнем якорях устанавливается родительская AnchorPane. Я пробовал это с нижним прикрепленным и не прикрепленным. В идеале, я бы хотел вырастить анкерную панель, поскольку текстовое поле растет.

Я не против читать TextProperty и вычислять размер триггера, который я установил... но это кажется взломанным подходом, если есть уже лучшая практика. Количество свойств и вспомогательных объектов javafx достаточно пугающе, что кажется хорошим моментом, чтобы задать здесь вопрос, а не пытаться выяснить, сколько пикселей занимает шрифт/абзацы и т.д.

Update:

Поэтому я подумал, что, возможно, я его переусердствовал, и все, что мне нужно было сделать, это выключить полосы прокрутки, а остальное произойдет. Увы, поиск доступных полей и методов для "прокрутки", "вертикали", "vbar" не приносит ничего, что я могу использовать. ScrollTopProperty выглядит как нечто другое.

4b9b3361

Ответ 1

Проблема; высота 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);
    }
}

Но у него есть недостаток; высота текстовой области изменяется только в том случае, если между несколькими строками существуют разрывы строк (например, "Ввод ключей" ), если пользователь достаточно длинный, текст обернут в несколько строк, но высота не меняется.

Чтобы обойти этот недостаток, я добавил эту строку

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

после primaryStage.show();. Он хорошо работает для длинных типов, когда пользователь не разбивает строки. Однако это порождает еще одну проблему. Эта проблема возникает, когда пользователь удаляет текст, нажимая "backspace". Проблема возникает именно в том случае, если высота textHolder изменена и где высота textArea установлена ​​на новое значение. ИМО это может быть ошибка, не наблюдал глубже.

В обоих случаях копирование выполняется правильно.

Ответ 2

В ожидании лучшего, я использую это хакерское решение.

  • найдите вертикальную полосу прокрутки текстового поля.
  • сделать прозрачным
  • слушать его видимое свойство
  • Когда видится полоса прокрутки, я добавляю строку в текстовое поле.

Код:

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

Ответ 3

У меня была аналогичная проблема с созданием расширяющегося TextArea. Я создавал TextArea, который выглядит как TextField и расширяется вертикально каждый раз, когда в пространстве больше нет места. Я проверил все решения, которые я смог найти по этой теме в стеке и других доступных источниках. Я нашел несколько хороших решений, но ни один из них не был достаточно хорош.

После многих часов борьбы я понял этот подход.

Я расширил класс 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);
    };
  }