Как я могу сделать растяжку TextArea для заполнения контента, расширяя родительский процесс?
Итак, у меня есть TextArea, и когда пользователь вставляет в него абзацы или просто пишет в нем, я хочу, чтобы он расширялся вертикально, чтобы открыть весь доступный текст. То есть не использовать полосу прокрутки в самом текстовом поле... очень похоже на то, что происходит на многих веб-страницах. Многие пользователи, включая меня, не любят принудительно редактировать в маленьком окне. Точно, как работает окно обновления статуса Facebook.
Я пробовал
myTextArea.autoSize()
завернутый в
myTextArea.textProperty().addListener(new ChangeListener()....);
но это не сработает. Я думаю, что он доволен своим нынешним размером.
В левом, правом и верхнем якорях устанавливается родительская AnchorPane. Я пробовал это с нижним прикрепленным и не прикрепленным. В идеале, я бы хотел вырастить анкерную панель, поскольку текстовое поле растет.
Я не против читать TextProperty и вычислять размер триггера, который я установил... но это кажется взломанным подходом, если есть уже лучшая практика. Количество свойств и вспомогательных объектов javafx достаточно пугающе, что кажется хорошим моментом, чтобы задать здесь вопрос, а не пытаться выяснить, сколько пикселей занимает шрифт/абзацы и т.д.
Update:
Поэтому я подумал, что, возможно, я его переусердствовал, и все, что мне нужно было сделать, это выключить полосы прокрутки, а остальное произойдет. Увы, поиск доступных полей и методов для "прокрутки", "вертикали", "vbar" не приносит ничего, что я могу использовать. ScrollTopProperty выглядит как нечто другое.
Ответы
Ответ 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);
};
}