JavaFX TextArea и autoscroll

Я пытаюсь получить TextArea для автоматического прокрутки до конца с новым текстом, который помещается через обработчик события. Каждая новая запись представляет собой только одну длинную строку текста с каждой записью, разделенной разрывом строки. Я попробовал обработчик изменений, который устанавливает setscrolltop в Double.MIN_VALUE, но безрезультатно. Любые идеи о том, как это можно сделать?

Ответ 1

Вы должны добавить слушателя к элементу TextArea для прокрутки вниз, когда значение изменяется:

@FXML private TextArea txa; 

...

txa.textProperty().addListener(new ChangeListener<Object>() {
    @Override
    public void changed(ObservableValue<?> observable, Object oldValue,
            Object newValue) {
        txa.setScrollTop(Double.MAX_VALUE); //this will scroll to the bottom
        //use Double.MIN_VALUE to scroll to the top
    }
});

Но этот прослушиватель не запускается при использовании метода setText(text), поэтому, если вы хотите запустить его после setText(text), используйте appendText(text) сразу после него:

txa.setText("Text into the textArea"); //does not trigger the listener
txa.appendText("");  //this will trigger the listener and will scroll the
                     //TextArea to the bottom

Это больше похоже на ошибку, как только setText() должен запускать прослушиватель changed, но это не так. Это обходной путь, который я использую сам, и надеюсь, что он вам поможет.

Ответ 2

txa.appendText( ") будет прокручиваться донизу без прослушивателя. Это становится проблемой, если вы хотите прокрутить назад, и текст постоянно обновляется. txa.setText(" ") помещает верхнюю полосу прокрутки вверху и то же самое относится.

Мое решение состояло в том, чтобы расширить класс TextArea, отменив тэг FXML от textArea до LogTextArea. Там, где это работает, это явно вызывает проблемы в построителе сцен, поскольку он не знает, что этот компонент

import javafx.scene.control.TextArea;
import javafx.scene.text.Font;

public class LogTextArea extends TextArea {

private boolean pausedScroll = false;
private double scrollPosition = 0;

public LogTextArea() {
    super();
}

public void setMessage(String data) {
    if (pausedScroll) {
        scrollPosition = this.getScrollTop();
        this.setText(data);
        this.setScrollTop(scrollPosition);
    } else {
        this.setText(data);
        this.setScrollTop(Double.MAX_VALUE);
    }
}

public void pauseScroll(Boolean pause) {
    pausedScroll = pause;
}

}

Ответ 3

Альтернатива этой странной ошибке setText без использования appendText

textArea.selectPositionCaret(textArea.getLength());
textArea.deselect(); //removes the highlighting

Ответ 4

Одно добавление, которое я бы добавил к jamesarbrown response, было бы именно для использования логического свойства, поэтому вы можете получить доступ к нему из FXML. Что-то вроде этого.

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TextArea;

public class LogTextArea extends TextArea {
    private final BooleanProperty pausedScrollProperty = new SimpleBooleanProperty(false);
    private double scrollPosition = 0;

    public LogTextArea() {
        super();
    }

    public void setMessage(String data) {
        if (isPausedScroll()) {
            scrollPosition = this.getScrollTop();
            this.setText(data);
            this.setScrollTop(scrollPosition);
        } else {
            this.setText(data);
            this.setScrollTop(Double.MAX_VALUE);
        }
    }

    public final BooleanProperty pausedScrollProperty() { return pausedScrollProperty; }
    public final boolean isPausedScroll() { return pausedScrollProperty.getValue(); }
    public final void setPausedScroll(boolean value) { pausedScrollProperty.setValue(value); }
}

Однако проблема с этим ответом заключается в том, что если вы получаете необоснованно большой объем ввода (что может произойти при извлечении журнала из потока ввода-вывода), поток javaFX будет блокироваться, потому что TextArea получает слишком много данных.