JavaFX TextArea和autoscroll

时间:2013-07-22 23:30:35

标签: java javafx-2

我试图让TextArea自动滚动到底部,新文本通过事件处理程序放入。每个新条目只是一个长字符串,每个条目用换行符分隔。我尝试过一个更改处理程序,它将setscrolltop设置为Double.MIN_VALUE,但无济于事。有关如何做到这一点的任何想法?

5 个答案:

答案 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的回答一样在侦听器中设置滚动量时,会发生以下情况:

  1. TextProperty已更新
  2. 调用您的侦听器,并设置滚动条
  3. doSet被称为
  4. textUpdated被称为
  5. 滚动条回到左上角

当您附加“”时,

  1. TextProperty已更新
  2. 调用您的侦听器,并设置滚动条

上面的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获取了太多数据。