JavaFXTextArea和autoscroll

JavaFXTextArea和autoscroll,java,javafx-2,Java,Javafx 2,我试图获得一个文本区域,通过事件处理程序将新文本自动滚动到底部。每个新条目只是一个长文本字符串,每个条目之间用换行符分隔。我尝试了一个将setscrolltop设置为Double.MIN_值的更改处理程序,但没有效果。您知道如何做到这一点吗?您必须在TextArea元素中添加一个侦听器,以便在其值更改时滚动到底部: @FXML private TextArea txa; ... txa.textProperty().addListener(new ChangeListener<Obj

我试图获得一个文本区域,通过事件处理程序将新文本自动滚动到底部。每个新条目只是一个长文本字符串,每个条目之间用换行符分隔。我尝试了一个将setscrolltop设置为Double.MIN_值的更改处理程序,但没有效果。您知道如何做到这一点吗?

您必须在
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
    }
});
这听起来更像是一个bug,一旦
setText()
应该触发
changed
监听器,但是它不会。这是我自己使用的解决方法,希望它能帮助您。

txa.appendText(“”)将在没有侦听器的情况下滚动到底部。如果您想向后滚动并且文本不断更新,这将成为一个问题。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;
}

}

不使用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获取了太多的数据。

我没有足够的声誉发表评论,但是我想给未来的读者一些启示,为什么setText看起来不会触发听者,而appendText会触发听者,就像Math的答案一样

我只是在自己遇到类似问题时找到了这个答案,并研究了代码。这是目前谷歌搜索中“javafx textarea settext scroll”的最高结果

setText确实会触发侦听器。 根据TextInputControl(TextArea的超类)中doSet方法上的javadoc:

在doSet方法中,对updateText()进行了调用,其中TextArea覆盖:

  @Override final void textUpdated() {
        setScrollTop(0);
        setScrollLeft(0);
    }  
因此,当您将侦听器中的滚动量设置为Math的答案时,会发生以下情况:

  • TextProperty已更新
  • 此时将调用您的侦听器,并设置滚动
  • 多塞特被称为
  • textUpdated被称为
  • 滚动条被设置回左上角
  • 然后在附加“”时

  • TextProperty已更新
  • 此时将调用您的侦听器,并设置滚动
  • 上面的javadoc清楚地说明了为什么会出现这种情况——只有在使用setText时才调用doSet。 事实上,appendText调用调用replaceText的insertText,而javadoc进一步声明replaceText不会触发对doSet的调用


    这种行为相当令人恼火,特别是因为这些都是最终的方法,乍一看并不明显,但不是一个bug。

    setScrollTop(Double.MIN_值)滚动到顶部,而
    MAX_VALUE
    滚动到底部。@AdamJensen我在这里做一个测试,因为我不久前回答了这个问题,只是看了不记得了。谢谢你的报告。@AdamJensen你完全正确。我刚修好。谢谢我建议在Platform.runLater()中设置滚动条——我的文本区域有时会错过一些没有滚动条的滚动条更新。我似乎无法让它工作,我的文本区域只是一直滚动到顶部。有什么想法吗?两者都使用
    Platform.runLater(()->this.setScrollTop(Double.MAX_值))
    设置Crolltop(双最大值)
    。此外,我的文本区域似乎很模糊。
         * 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.
    
      @Override final void textUpdated() {
            setScrollTop(0);
            setScrollLeft(0);
        }