Reputation: 51911
JavaFX: Is it possible to bind TextArea
height (row count) to the height of its content?
I would like to dynamically change height of TextArea
while writing the text.
Upvotes: 10
Views: 7236
Reputation: 672
Here is a simple solution that queries the content's height and sets the preferred height of the textarea. I made it a custom component that can be reused easily:
public class AutoSizedTextArea extends TextArea {
public AutoSizedTextArea() {
super();
getStyleClass().add("AutoSizedTextArea");
setWrapText(true);
Platform.runLater(() -> {
var textElement = lookup(".text");
textProperty().addListener((observable, oldValue, newValue) -> {
setPrefHeight(textElement.getLayoutBounds().getHeight());
});
});
}
}
Hiding the scrollbars was necessary, otherwise it changes width for a moment when you type at the end of a line.
// removing unnecessary inner padding and background effects
.AutoSizedTextArea .content {
-fx-padding: 0px;
-fx-background-color: transparent;
}
.AutoSizedTextArea {
-fx-background-color: transparent;
}
// removing scrollbars
.AutoSizedTextArea .scroll-bar:vertical {
-fx-pref-width: 1;
-fx-opacity: 0;
}
.AutoSizedTextArea .scroll-bar:horizontal {
-fx-pref-height: 1;
-fx-opacity: 0;
}
Upvotes: 0
Reputation: 608
This is an exact, simple & working solution:
SimpleIntegerProperty count = new SimpleIntegerProperty(20);
int rowHeight = 10;
txtArea.prefHeightProperty().bindBidirectional(count);
txtArea.minHeightProperty().bindBidirectional(count);
txtArea.scrollTopProperty().addListener(new ChangeListener<Number>(){
@Override
public void changed(ObservableValue<? extends Number> ov, Number oldVal, Number newVal) {
if(newVal.intValue() > rowHeight){
count.setValue(count.get() + newVal.intValue());
}
}
});
Alternatively you can use lambdas to simply the syntax even further:
SimpleIntegerProperty count=new SimpleIntegerProperty(20);
int rowHeight = 10;
textArea.prefHeightProperty().bindBidirectional(count);
textArea.minHeightProperty().bindBidirectional(count);
textArea.scrollTopProperty().addListener((ov, oldVal, newVal) -> {
if(newVal.intValue() > rowHeight){
count.setValue(count.get() + newVal.intValue());
}
});
Upvotes: 2
Reputation: 3923
A solution that workq fine in javafx8 (hiding toolbar is inspired from JavaFX TextArea Hiding Scroll Bars):
class MyTextArea extends TextArea {
@Override
protected void layoutChildren() {
super.layoutChildren();
ScrollBar scrollBarv = (ScrollBar) this.lookup(".scroll-bar:vertical");
if (scrollBarv != null) {
System.out.println("hiding vbar");
((ScrollPane) scrollBarv.getParent()).setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
}
ScrollBar scrollBarh = (ScrollBar) this.lookup(".scroll-bar:horizontal");
if (scrollBarh != null) {
System.out.println("hiding hbar");
((ScrollPane) scrollBarh.getParent()).setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
}
}
@Override
protected double computePrefWidth(double width) {
Bounds bounds = getTextBounds();
Insets insets = getInsets();
double w = Math.ceil(bounds.getWidth() + insets.getLeft() + insets.getRight());
return w;
}
@Override
protected double computePrefHeight(double height) {
Bounds bounds = getTextBounds();
Insets insets = getInsets();
double h = Math.ceil(bounds.getHeight() + insets.getLeft() + insets.getRight());
return h;
}
//from https://stackoverflow.com/questions/15593287/binding-textarea-height-to-its-content/19717901#19717901
public Bounds getTextBounds() {
//String text = (textArea.getText().equals("")) ? textArea.getPromptText() : textArea.getText();
String text = "";
text = this.getParagraphs().stream().map((p) -> p + "W\n").reduce(text, String::concat);
text += "W";
helper.setText(text);
helper.setFont(this.getFont());
// Note that the wrapping width needs to be set to zero before
// getting the text's real preferred width.
helper.setWrappingWidth(0);
return helper.getLayoutBounds();
}
}
Upvotes: 1
Reputation: 999
Have a look at JavaFX utility class. Although this is not a solution using binding, computeTextHeight(Font font, String text, double wrappingWidth)
method can help you.
Upvotes: 3