我试图让TextArea自动滚动到底部,新文本通过事件处理程序放入。每个新条目只是一个长字符串,每个条目用换行符分隔。我尝试过一个更改处理程序,它将setscrolltop设置为Double.MIN_VALUE,但无济于事。有关如何做到这一点的任何想法?
答案 0 :(得分:25)
您必须向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
监听器,但事实并非如此。这是我自己使用的解决方法,希望它可以帮助你。
答案 1 :(得分:10)
txa.appendText(“”)将滚动到没有监听器的底部。如果要向后滚动并且文本不断更新,这将成为一个问题。 txa.setText(“”)将滚动条放回顶部,同样的问题也适用。
我的解决方案是扩展TextArea类,将textArea的FXML标记修改为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;
}
}
答案 2 :(得分:5)
替代那个奇怪的setText错误而不使用appendText
textArea.selectPositionCaret(textArea.getLength());
textArea.deselect(); //removes the highlighting
答案 3 :(得分:4)
我没有足够的声誉来发表评论,但是我想为以后的读者提供一些见解,以了解为何setText似乎不触发监听器,而appendText却如此,就像Math的回答一样。
我自己遇到类似问题时就找到了这个答案,并调查了代码。目前,这是Google搜索中“ javafx textarea settext scroll”的最高搜索结果。
setText确实会触发侦听器。 根据TextInputControl(TextArea的超类)的doSet方法的javadoc:
mat3(viewMatrix)
在doSet方法中,对updateText()进行了调用,该方法被TextArea覆盖:
* doSet is called whenever the setText() method was called directly
* on the TextInputControl, or when the text property was bound,
* unbound, or reacted to a binding invalidation. It is *not* called
* when modifications to the content happened indirectly, such as
* through the replaceText / replaceSelection methods.
因此,当您像Math的回答一样在侦听器中设置滚动量时,会发生以下情况:
当您附加“”时,
上面的javadoc很清楚为什么是这种情况-仅在使用setText时才调用doSet。 实际上,appendText调用insertText,后者调用replaceText,并且javadoc进一步声明replaceText不会触发对doSet的调用。
这种行为非常令人讨厌,特别是因为这些都是最终方法,乍一看并不明显-但这不是错误。
答案 4 :(得分:2)
我要添加到jamesarbrown的一个附录中,这将是使用布尔属性,因此您可以从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); }
}
然而,这个答案的问题在于,如果你被大量输入淹没(从IO流中检索日志时会发生这种情况),javaFX线程将锁定,因为TextArea获取了太多数据。