Я пытаюсь получить TextArea для автоматического прокрутки до конца с новым текстом, который помещается через обработчик события. Каждая новая запись представляет собой только одну длинную строку текста с каждой записью, разделенной разрывом строки. Я попробовал обработчик изменений, который устанавливает setscrolltop в Double.MIN_VALUE, но безрезультатно. Любые идеи о том, как это можно сделать?
JavaFX TextArea и autoscroll
Ответ 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 получает слишком много данных.