javajavafxresizedividersplitpane

How to detect mouse-dragged event on SplitPane Divider in JavaFX


I'm using SplitPane in JavaFx to split two nodes with a divider. I am looking for a way to differentiate between two cases. One case when the divider is moving responsively on screen width changes, second case is when I'm using the mouse to drag the divider to a certain position. I need to add a listener to the divider when it's dragged using the mouse. How can I do that?


Solution

  • What you need is a way to know when the change in divider position is the result of a window resize. Or, more formally, a Scene resize.

    You can set a flag whenever the Scene is doing a layout, using addPreLayoutPulseListener, then clear that flag at the end of resize event processing using Platform.runLater. While that flag is true, you can assume any change in SplitPane divider position was due to the Scene layout, not due to user actions.

    Here’s an example:

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.geometry.Insets;
    import javafx.stage.Stage;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.SplitPane;
    import javafx.scene.layout.BorderPane;
    
    public class DividerListenerTest
    extends Application {
        private boolean windowResizing;
    
        @Override
        public void start(Stage stage) {
            Label left = new Label("Left");
            left.setMinSize(1, 1);
            left.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
            left.setPadding(new Insets(200));
    
            Label right = new Label("Right");
            right.setMinSize(1, 1);
            right.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
            right.setPadding(new Insets(200));
    
            SplitPane splitPane = new SplitPane(left, right);
    
            splitPane.getDividers().get(0).positionProperty().addListener(
                (o, oldPos, newPos) -> dividerResized(oldPos, newPos));
    
            Scene scene = new Scene(new BorderPane(splitPane));
            scene.addPreLayoutPulseListener(() -> {
                windowResizing = true;
                Platform.runLater(() -> {
                    windowResizing = false;
                });
            });
    
            stage.setScene(scene);
            stage.setTitle("Divider Listener Test");
            stage.show();
        }
    
        private void dividerResized(Number oldPos,
                                    Number newPos) {
            if (windowResizing) {
                return;
            }
    
            System.out.println("User moved divider position to " + newPos);
        }
    
        public static class Main {
            public static void main(String[] args) {
                Application.launch(DividerListenerTest.class, args);
            }
        }
    }
    

    Note that addPreLayoutPulseListener was added as of JavaFX 9, and isn’t available in older versions.

    Java 8

    For older versions of JavaFX, you can accomplish the same thing by overriding the layoutChildren() method of your Scene’s root:

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.geometry.Insets;
    import javafx.stage.Stage;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.SplitPane;
    import javafx.scene.layout.BorderPane;
    
    public class DividerListenerTest
    extends Application {
        private boolean windowResizing;
    
        @Override
        public void start(Stage stage) {
            Label left = new Label("Left");
            left.setMinSize(1, 1);
            left.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
            left.setPadding(new Insets(200));
    
            Label right = new Label("Right");
            right.setMinSize(1, 1);
            right.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
            right.setPadding(new Insets(200));
    
            SplitPane splitPane = new SplitPane(left, right);
    
            splitPane.getDividers().get(0).positionProperty().addListener(
                (o, oldPos, newPos) -> dividerResized(oldPos, newPos));
    
            Scene scene = new Scene(new BorderPane(splitPane) {
                @Override
                protected void layoutChildren() {
                    windowResizing = true;
                    super.layoutChildren();
                    Platform.runLater(() -> {
                        windowResizing = false;
                    });
                }
            });
    
            stage.setScene(scene);
            stage.setTitle("Divider Listener Test");
            stage.show();
        }
    
        private void dividerResized(Number oldPos,
                                    Number newPos) {
            if (windowResizing) {
                return;
            }
    
            System.out.println("User moved divider position to " + newPos);
        }
    }