4

I have a very costly action to do on a mouse scroll on a pane. I currently use

pane.setOnScroll({myMethod()}).

The problem is that if you scroll a lot it computes everything many times. So what I want is to do my actions only when the scroll is finished. I hoped to use setOnScrollStarted, save the starting value and setOnScrollFinished to do my actions.

But I don't know why these two methods are never called. As a test I used

pane.setOnScroll({System.out.println("proof of action"});

and it was clearly never called.

Any idea on how to call my method only at the end of the scroll?

Thanks in advance, A

DVarga
  • 21,311
  • 6
  • 55
  • 60
  • 1
    The code sample in your third paragraph won't compile, you're missing a closing parenthesis. – Reinstate Monica Nov 28 '16 at 11:23
  • The scroll started and the scroll finished is not fired because they are only fired when the scroll was performed by a touch gesture (not with e.g. mouse wheel). – DVarga Nov 28 '16 at 11:24

1 Answers1

4

From the javadoc of ScrollEvent (emphasis mine):

When the scrolling is produced by a touch gesture (such as dragging a finger over a touch screen), it is surrounded by the SCROLL_STARTED and SCROLL_FINISHED events. Changing number of involved touch points during the scrolling is considered a new gesture, so the pair of SCROLL_FINISHED and SCROLL_STARTED notifications is delivered each time the touchCount changes. When the scrolling is caused by a mouse wheel rotation, only a one-time SCROLL event is delivered, without the started/finished surroundings.

A possible workaround:

Increment a counter variable every time a scroll is detected. In the listener start a new thread that waits 1 second and performs the action that you want only if the counter equals to 1 (the last scrolling) then decrements the counter.

I created a Gist, but I copy here the code:

public class ScrollablePane extends Pane {
    private Integer scrollCounter = 0;

    private final ObjectProperty<EventHandler<? super ScrollEvent>> onScrollEnded = new SimpleObjectProperty<>();

    public final ObjectProperty<EventHandler<? super ScrollEvent>> onScrollEndedProperty() {
        return onScrollEnded;
    }

    public ScrollablePane() {
        this.setOnScroll(e -> {
            scrollCounter++;

            Thread th = new Thread(() -> {
                try {
                    Thread.sleep(1000);
                    if (scrollCounter == 1)
                        onScrollEnded.get().handle(e);

                    scrollCounter--;
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            });
            th.setDaemon(true);
            th.start();
        });
    }

    public void setOnScrollEnded(EventHandler<? super ScrollEvent> handler) {
        onScrollEnded.setValue(handler);
    }
}

To use it:

public class MyApplication extends Application {


    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = new BorderPane();
            Scene scene = new Scene(root, 400, 400);

            ScrollablePane pane = new ScrollablePane();
            pane.setOnScrollEnded(e -> System.out.println("Scroll just has been ended"));

            root.setCenter(pane);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
DVarga
  • 21,311
  • 6
  • 55
  • 60