madox2
madox2

Reputation: 51911

Binding TextArea height to its content

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

Answers (4)

Ambrus Tóth
Ambrus Tóth

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

Ruturaj Patil
Ruturaj Patil

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

Daniel
Daniel

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

Ramazan
Ramazan

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

Related Questions