【问题标题】:JavaFX TextArea and autoscrollJavaFX TextArea 和自动滚动
【发布时间】:2013-07-21 20:55:24
【问题描述】:

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

【问题讨论】:

    标签: java javafx-2


    【解决方案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 侦听器,但它不会。这是我自己使用的解决方法,希望对您有所帮助。

    【讨论】:

    • setScrollTop(Double.MIN_VALUE); 滚动到顶部,而MAX_VALUE 滚动到底部。
    • @AdamJensen 我会在这里做一个测试,因为我前段时间回答过这个问题,我只是看不起来。感谢您的报告。
    • @AdamJensen 你是绝对正确的。我刚刚修好了。谢谢!
    • 我建议在 Platform.runLater() 中设置滚动——如果没有它,我的文本区域有时会错过一些滚动更新。
    • 我似乎无法让它工作,我的 TextArea 只是一直滚动到顶部。有任何想法吗?两者都使用Platform.runLater(() -&gt; this.setScrollTop(Double.MAX_VALUE));setScrollTop(Double.MAX_VALUE)。此外,我的 Textarea 似乎很模糊。
    【解决方案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 确实如此,就像 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 的答案在侦听器中设置滚动量时,会发生以下情况:

      1. TextProperty 已更新
      2. 你的监听器被调用,并且滚动被设置
      3. doSet 被调用
      4. textUpdated 被调用
      5. 滚动条被设置回左上角

      当你然后附加“”时,

      1. TextProperty 已更新
      2. 你的监听器被调用,并且滚动被设置

      上面的 javadoc 很清楚为什么会出现这种情况 - doSet 仅在使用 setText 时调用。 事实上,appendText 调用 insertText 调用 replaceText - 并且 javadoc 进一步指出 replaceText 不会触发对 doSet 的调用。

      这种行为相当烦人,尤其是因为这些都是最终方法,乍一看并不明显 - 但不是错误。

      【讨论】:

        【解决方案4】:

        不使用 appendText 替代那个奇怪的 setText 错误

        textArea.selectPositionCaret(textArea.getLength());
        textArea.deselect(); //removes the highlighting
        

        【讨论】:

          【解决方案5】:

          我要添加到 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 获取了太多数据。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-12-21
            • 2016-11-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多