JavaFX TextArea and autoscroll

匿名 (未验证) 提交于 2019-12-03 02:13:02

问题:

I am trying to get a TextArea to autoscroll to the bottom with new text which is put in via an event handler. Each new entry is just one long string of text with each entry separated by a line break. I have tried a change handler which sets setscrolltop to Double.MIN_VALUE but to no avail. Any ideas of how this could be done?

回答1:

You have to add a listener to the TextArea element to scroll to the bottom when it's value is changed:

@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     } }); 

But this listener is not triggered when you use the setText(text) method, so if you want to trigger it after a setText(text) use the appendText(text) right after it:

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 

This sounds more like a bug, once the setText() should trigger the changed listener, however it doesn't. This is the workaround I use myself and hope it helps you.



回答2:

txa.appendText("") will scroll to the bottom without a listener. This becomes an issue if you want to scroll back and the text is being constantly updated. txa.setText("") puts the scroll bar back at the top and same issue applies.

My solution was to extend the TextArea class, ammend the FXML tag from textArea to LogTextArea. Where this works, it clearly causes problems in scene builder as it does not know what this component is

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:

Alternative to that strange setText bug without using appendText

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


回答4:

One addendum I would add to jamesarbrown's response would be to this would be to use a boolean property instead so you can access it from within FXML. Something like this.

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); } } 

However, the problem with this answer is that if you get flooded with an unreasonably large amount of input (as can happen when retrieving a log from an IO Stream) the javaFX thread will lock up because the TextArea gets too much data.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!