【发布时间】:2013-07-21 20:55:24
【问题描述】:
我正在尝试使用通过事件处理程序放入的新文本让 TextArea 自动滚动到底部。每个新条目只是一长串文本,每个条目由换行符分隔。我尝试了一个更改处理程序,它将 setscrolltop 设置为 Double.MIN_VALUE 但无济于事。关于如何做到这一点的任何想法?
【问题讨论】:
我正在尝试使用通过事件处理程序放入的新文本让 TextArea 自动滚动到底部。每个新条目只是一长串文本,每个条目由换行符分隔。我尝试了一个更改处理程序,它将 setscrolltop 设置为 Double.MIN_VALUE 但无济于事。关于如何做到这一点的任何想法?
【问题讨论】:
您必须为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 侦听器,但它不会。这是我自己使用的解决方法,希望对您有所帮助。
【讨论】:
setScrollTop(Double.MIN_VALUE); 滚动到顶部,而MAX_VALUE 滚动到底部。
Platform.runLater(() -> this.setScrollTop(Double.MAX_VALUE)); 或setScrollTop(Double.MAX_VALUE)。此外,我的 Textarea 似乎很模糊。
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;
}
}
【讨论】:
我没有足够的声誉来发表评论,但想为未来的读者提供一些见解,说明为什么 setText 似乎没有触发听众,但 appendText 确实如此,就像 Math 的回答一样。
我自己在遇到类似问题时找到了这个答案,并查看了代码。这是目前谷歌搜索中“javafx textarea settext scroll”的最高结果。
setText 确实会触发侦听器。 根据TextInputControl(TextArea的超类)中doSet方法的javadoc:
* 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.
在 doSet 方法中,调用了 updateText(),TextArea 将覆盖它:
@Override final void textUpdated() {
setScrollTop(0);
setScrollLeft(0);
}
因此,当您按照 Math 的答案在侦听器中设置滚动量时,会发生以下情况:
当你然后附加“”时,
上面的 javadoc 很清楚为什么会出现这种情况 - doSet 仅在使用 setText 时调用。 事实上,appendText 调用 insertText 调用 replaceText - 并且 javadoc 进一步指出 replaceText 不会触发对 doSet 的调用。
这种行为相当烦人,尤其是因为这些都是最终方法,乍一看并不明显 - 但不是错误。
【讨论】:
不使用 appendText 替代那个奇怪的 setText 错误
textArea.selectPositionCaret(textArea.getLength());
textArea.deselect(); //removes the highlighting
【讨论】:
我要添加到 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 获取了太多数据。
【讨论】: