Marcel
Marcel

Reputation: 1594

Make a TextArea grow with TextHeight in JavaFX (Rows/Lines)

I have been trying to make the TextArea grow with its content, for exmaple:

There is one Line in the Text area, now the user keeps writing and reaches the TextArea's right border and the text wraps, now he does have a second line (which is not wrapped by simply using '\n' internally) and I now want the text field to grow for the height of one more line.

What I have tried already:

This

Text text = textArea.lookup(".text").getLocalBounds.getHeight()

always returns the same, no matter how much Lines i do have.

This

textArea.getPrefRowCount()

always returns

1

no matter how much lines I have.

How can I achieve this? If someone wants a working example, Skype has that kind of mechanism in its chat.

Upvotes: 0

Views: 1454

Answers (3)

M00her
M00her

Reputation: 1

From here Binding height of internal Text node to the height of TextArea

I made a working solution. TextArea is being expanded according to the number of text rows. Bug with scroll bar that was mentioned also is fixed by simply adding a small padding which prevents internal ScrollBar in TextArea from spawning.

First of all:

textArea.setWrapText(true);

Then we need to call this scary looking method with listeners:

private void setupTextAreaDimensionsChangeEventListener(TextArea textArea) {
    textArea.textProperty().addListener((observable, oldValue, newValue) -> {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        executorService.schedule(() -> {
            updateTextAreaHeight(textArea);
        }, 10, TimeUnit.MILLISECONDS);
    });
    textArea.widthProperty().addListener((observableValue, oldWidth, newWidth) -> {
        updateTextAreaHeight(textArea);
    });
}

First listener handles vertical growth caused by typing and reaching end of the row. Second listener is responsible for width TextArea's width changes for example due to resizing a window. ScheduledExecutorService is for making sure that internal Text node used in next method is already reachable (Platform run later wasn't enough).

private void updateTextAreaHeight(TextArea textArea) {
    Platform.runLater(() -> {
        try {
            Text text = (Text) textArea.lookup(".text");
            if (text != null) {
                double newHeight = calculateRequiredHeight(text);
                updateTextAreaDimensions(textArea, newHeight);
            } else {
                throw new Exception("Failed to locate Text Node inside of TextArea");
            }
        } catch (Exception e) {
            System.err.println("Failed to adjust height of TextArea: " + e);
        }
    });
}

private double calculateRequiredHeight(Text text) {
    double textHeight = text.boundsInParentProperty().get().getMaxY();
    return textHeight + PADDING;
}

private void updateTextAreaDimensions(TextArea textArea, double height) {
    textArea.setMinHeight(height);
    textArea.setPrefHeight(height);
    textArea.setMaxHeight(height);
}

All together makes a responsive TextArea.

Upvotes: 0

Marcel
Marcel

Reputation: 1594

This

Text text = (Text) textArea.lookup(".text");
textArea.setPrefHeight(text.boundsInParentProperty().get().getMaxY());

works but is not very nice since the scroll bars keep bugging.

Upvotes: 1

Andrei Mesh
Andrei Mesh

Reputation: 275

Should not be too hard to do it. I think you will probably have to @Override the setPreferredRowCount() method of textArea to return the number of rows based on what you need, or on a fixed value.

This is a common "issue" u can encounter on any Swing component such as JFrame, JPanel, JLabel, JButton, etc.

Also on these components too it is highly recommended to override the preferred methods in order to correctly resize them, instead of using setBounds.

Also maybe the setPreferredRowCount() method won't update herself so you would probably have to call it in a loop (or a Thread but I'd rather not) to make sure your sizes are always updated. Something like:

EDIT:

while(inputNeeded) {
    if(textArea1.textChanged) {
        if(text1.length > 10) {
            textArea1.setPreferredRowCount(3);
        } else if(text1.length > 20) {
            textArea1.setPreferredRowCount(4);
        }
    }
}

... and so on. My bad, you don't really have to override anything. Just try to use a loop like this.

Upvotes: -1

Related Questions