Drag and scroll automatically in your application

One of the first feature I’ve done for the SpreadsheetView is to enable the scroll when you click & drag for selection and you go out of the grid.

This can easily be achieved. I’ll show how I’ve done it on the SpreadsheetView, but it can be transposed to other controls easily.

First you need to create a Timer that will be started when a drag is detected, and turned of when the drag is done.

private final EventHandler<MouseEvent> onDragDetectedEventHandler = new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent mouseEvent) {
//cellsView refers to the inner TableView.
            cellsView.addEventHandler(MouseEvent.MOUSE_RELEASED, dragDoneHandler);
            timer.setCycleCount(Timeline.INDEFINITE);
            timer.play();
        }
    };
/**
     * When the drag is over, we remove the listener and stop the timer
     */
    private final EventHandler<MouseEvent> dragDoneHandler = new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent mouseEvent) {
            timer.stop();
            spreadsheetView.removeEventHandler(MouseEvent.MOUSE_RELEASED, this);
        }
    };

And in the constructor, you initialize the Timer and add the handlers previously defined :

 timer = new Timeline(new KeyFrame(Duration.millis(100), timerEventHandler));
cellsView.setOnDragDetected(onDragDetectedEventHandler);

So you see that I only add the dragDoneHandler when the I detect a drag. And I carefully remove it when it’s called.

Now let’s look at our timerEventHandler :

private final EventHandler<ActionEvent> timerEventHandler = (ActionEvent event) -> {
        GridViewSkin skin = (GridViewSkin) getCellsViewSkin();
        //If my event is not within the grid, it means I have to scroll.
        if (mouseEvent != null && !cellsView.contains(mouseEvent.getX(), mouseEvent.getY())) {
            //Pick up the current mouse location.
            double sceneX = mouseEvent.getSceneX();
            double sceneY = mouseEvent.getSceneY();
            //Pick up the Grid top-left location.
            double layoutX = cellsView.getLocalToSceneTransform().getTx();
            double layoutY = cellsView.getLocalToSceneTransform().getTy();
            //Pick up Grid bottom-right location.
            double layoutXMax = layoutX + cellsView.getWidth();
            double layoutYMax = layoutY + cellsView.getHeight();

            //If I go out of bounds, simply scroll.
            if (sceneX > layoutXMax) {
                skin.getHBar().increment();
            } else if (sceneX < layoutX) {
                skin.getHBar().decrement();
            }
            //Note the separate "if" here in order 
            //to scroll in both direction if necessary
            if (sceneY > layoutYMax) {
                skin.getVBar().increment();
            } else if (sceneY < layoutY) {
                skin.getVBar().decrement();
            }
        }
    };

The code speaks for itself. I just did an unconventional thing here to access the scrollBars. They actually are hidden within the VirtualFlow so I have to access the skin to retrieve them.

Cheers

Leave a Reply

Your email address will not be published. Required fields are marked *