JavaFX 2: Making a ScrollPane automatically scroll to the edge after adding content

纵然是瞬间 提交于 2019-12-04 13:59:58

问题


Using JavaFX 2, I have a basic example of a ScrollPane that contains an HBox of Labels. I want to be able to add a Label to the HBox, and simultaneously scroll to the right edge of the ScrollPane so that the newly added Label is visible. My current method uses the setHvalue() to set the scroll position and getHmax() to get the maximum scrolling distance allowed.

The problem is that when I set the scroll position using getHmax(), it is as if the just-added Label is not computed in the ScrollPanel's scroll width. Is there a way that I can update this inner width before trying setHvalue?

Please see this simple example code which exhibits the issue.

In particular, please notice the addChatItem(String item) method, which contains the logic for scrolling to the edge of the ScrollPane.

    import java.util.Timer;
    import java.util.TimerTask;

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.StackPane;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;

    public class ScrollPaneTest extends Application {   
        static int defaultFontSize = 30;

        ScrollPaneTest scrollPaneTest = this;

        ScrollPane chatBoxScrollPane = new ScrollPane();
        HBox chatBox = new HBox();
        Chatter chatter = new Chatter();

        public static void main(String[] args) {
            launch(args);//default
        }

        @Override
        public void stop() throws Exception {
            super.stop();
            System.exit(0);
        }

        @Override
        public void start(Stage primaryStage) {
            BorderPane borderPane = new BorderPane();       

            StackPane chatBoxStackPane = new StackPane();

            chatBoxScrollPane.setContent(chatBox);
            //chatBoxScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
            chatBoxScrollPane.setMaxHeight(50);

            chatBoxStackPane.getChildren().add(chatBoxScrollPane);

            borderPane.setCenter(chatBoxStackPane);


            Scene scene = new Scene(borderPane, 800, 600);
            primaryStage.setScene(scene);
            primaryStage.setTitle("Scroll Demo");
            primaryStage.show(); 


            new Thread("mainGameControlThread") {
                public void run() {
                    chatter.chatLoop(scrollPaneTest);
                }
            }.start();
        }

        public void addChatItem(String chatString) {
            Label title = new Label(chatString);        
            title.setFont(new Font("Verdana", defaultFontSize));

            chatBox.getChildren().add(title);

            chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax());
        }

        class Chatter {
            public void chatLoop(final ScrollPaneTest test) {

                Timer closingCeremonyTimer = new Timer();
                closingCeremonyTimer.schedule(new TimerTask() {
                    public void run() {
                        Platform.runLater(new Runnable() {
                            @Override 
                            public void run() {
                                test.addChatItem("Hello World. ");
                            }
                        });
                        chatLoop(test);
                    }
                }, (long) (0.5*1000));
            }
        }
    }

Here is an image of issue, notice how the ScrollPane has not scrolled to the right edge.

Edit:

I've come up with a workaround but it is very far from ideal. My solution is to start a timer which will use setHvalue() after enough time has passed for the ScrollPane to discover the true width of its content. my addChatItem() method now looks like this:

public void addChatItem(String chatString) {
    Label title = new Label(chatString);        
    title.setFont(new Font("Verdana", defaultFontSize));

    chatBox.getChildren().add(title);

    Timer closingCeremonyTimer = new Timer();       
    closingCeremonyTimer.schedule(new TimerTask() {
        public void run() {
            chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax());
        }
    }, (long) 50);
}

Unfortunately, the number 50 in that method needs to be greater than the time that it takes the ScrollPane to update its inner content's width, and that seems to be far from guaranteed.


回答1:


you can bind to Hbox widthproperty chnages .

Sample Code :

   //in start method add this code
    DoubleProperty wProperty = new SimpleDoubleProperty();
    wProperty.bind(chatBox.widthProperty()); // bind to Hbox width chnages
    wProperty.addListener(new ChangeListener() {
        @Override
        public void changed(ObservableValue ov, Object t, Object t1) {
           //when ever Hbox width chnages set ScrollPane Hvalue
         chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax()); 
        }
    }) ;

and

 // remove below line from your addChatItem() method   
 chatBoxScrollPane.setHvalue(chatBoxScrollPane.getHmax());

Result :yes added numbering for fun ;)




回答2:


In my case I needed to enclose a HBox called labels inside a ScrollPane called msgs and here is the way I handled autoscroll.

NOTE: The changeListener added to labels HBox is implemented via Lambda syntax(available in JDK8)

labels.heightProperty().addListener((observable, oldVal, newVal) ->{
        msgs.setVvalue(((Double) newVal).doubleValue());
    });


来源:https://stackoverflow.com/questions/16992731/javafx-2-making-a-scrollpane-automatically-scroll-to-the-edge-after-adding-cont

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