Javafx 2 click and double click

前端 未结 9 1432
盖世英雄少女心
盖世英雄少女心 2020-12-01 06:00

I would like to know if it was possible to detect the double-click in JavaFX 2 ? and how ?

I would like to make different event between a click and a double click.

相关标签:
9条回答
  • 2020-12-01 06:42

    Since it is not possible to distinguish between single-click and double-click by default, we use the following approach:

    On single-click, we wrap the single-click operation in an abortable runnable. This runnable waits a certain amount of time (i.e., SINGLE_CLICK_DELAY) before being executed.

    In the meantime, if a second click, i.e., a double-click, occurs, the single-click operation gets aborted and only the double-click operation is performed.

    This way, either the single-click or the double-click operation is performed, but never both.


    Following is the full code. To use it, only the three TODO lines have to be replaced by the wanted handlers.

    private static final int SINGLE_CLICK_DELAY = 250;
    private ClickRunner latestClickRunner = null;
    
    private class ClickRunner implements Runnable {
    
        private final Runnable onSingleClick;
        private boolean aborted = false;
    
        public ClickRunner(Runnable onSingleClick) {
            this.onSingleClick = onSingleClick;
        }
    
        public void abort() {
            this.aborted = true;
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(SINGLE_CLICK_DELAY);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!aborted) {
                System.out.println("Execute Single Click");
                Platform.runLater(() -> onSingleClick.run());
            }
        }
    }
    
    private void init() {
        container.setOnMouseClicked(me -> {
            switch (me.getButton()) {
                case PRIMARY:
                    if (me.getClickCount() == 1) {
                        System.out.println("Single Click");
                        latestClickRunner = new ClickRunner(() -> {
                          // TODO: Single-left-click operation
                        });
                        CompletableFuture.runAsync(latestClickRunner);
                    }
                    if (me.getClickCount() == 2) {
                        System.out.println("Double Click");
                        if (latestClickRunner != null) {
                            System.out.println("-> Abort Single Click");
                            latestClickRunner.abort();
                        }
                        // TODO: Double-left-click operation
                    }
                    break;
                case SECONDARY:
                    // TODO: Right-click operation
                    break;
                default:
                    break;
            }
        });
    }
    
    0 讨论(0)
  • 2020-12-01 06:46

    Not sure if someone still follows this OP or refer it, but below is my version of differentiating single click to double click. While most of the answers are quite acceptable, it would be really useful if it can be done in a proper resuable way.

    One of the challenge I encountered is the need to have the single-double click differentiation on multiple nodes at multiple places. I cannot do the same repetitive cumbersome logic on each and every node. It should be done in a generic way.

    So I opted to implement a custom EventDispatcher and use this dispatcher on node level or I can apply it directly to Scene to make it applicable for all child nodes.

    For this I created a new MouseEvent namely 'MOUSE_DOUBLE_CLICKED", so tthat I am still sticking with the standard JavaFX practises. Now I can include the double_clicked event filters/handlers just like other mouse event types.

    node.addEventFilter(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e->{..<code to handle double_click>..});
    node.addEventHandler(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e->{..<code to handle double_click>..});
    

    Below is the implementation and complete working demo of this custom event dispatcher.

    import javafx.animation.KeyFrame;
    import javafx.animation.Timeline;
    import javafx.application.Application;
    import javafx.event.*;
    import javafx.geometry.Pos;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.StackPane;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    public class DoubleClickEventDispatcherDemo extends Application {
    
        @Override
        public void start(Stage stage) throws Exception {
            Rectangle box1 = new Rectangle(150, 150);
            box1.setStyle("-fx-fill:red;-fx-stroke-width:2px;-fx-stroke:black;");
            addEventHandlers(box1, "Red Box");
    
            Rectangle box2 = new Rectangle(150, 150);
            box2.setStyle("-fx-fill:yellow;-fx-stroke-width:2px;-fx-stroke:black;");
            addEventHandlers(box2, "Yellow Box");
    
            HBox pane = new HBox(box1, box2);
            pane.setSpacing(10);
            pane.setAlignment(Pos.CENTER);
            addEventHandlers(pane, "HBox");
    
            Scene scene = new Scene(new StackPane(pane), 450, 300);
            stage.setScene(scene);
            stage.show();
    
            // SETTING CUSTOM EVENT DISPATCHER TO SCENE
            scene.setEventDispatcher(new DoubleClickEventDispatcher(scene.getEventDispatcher()));
        }
    
        private void addEventHandlers(Node node, String nodeId) {
            node.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> System.out.println("" + nodeId + " mouse clicked filter"));
            node.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("" + nodeId + " mouse clicked handler"));
    
            node.addEventFilter(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e -> System.out.println("" + nodeId + " mouse double clicked filter"));
            node.addEventHandler(CustomMouseEvent.MOUSE_DOUBLE_CLICKED, e -> System.out.println(nodeId + " mouse double clicked handler"));
        }
    
        /**
         * Custom MouseEvent
         */
        interface CustomMouseEvent {
            EventType<MouseEvent> MOUSE_DOUBLE_CLICKED = new EventType<>(MouseEvent.ANY, "MOUSE_DBL_CLICKED");
        }
    
        /**
         * Custom EventDispatcher to differentiate from single click with double click.
         */
        class DoubleClickEventDispatcher implements EventDispatcher {
    
            /**
             * Default delay to fire a double click event in milliseconds.
             */
            private static final long DEFAULT_DOUBLE_CLICK_DELAY = 215;
    
            /**
             * Default event dispatcher of a node.
             */
            private final EventDispatcher defaultEventDispatcher;
    
            /**
             * Timeline for dispatching mouse clicked event.
             */
            private Timeline clickedTimeline;
    
            /**
             * Constructor.
             *
             * @param initial Default event dispatcher of a node
             */
            public DoubleClickEventDispatcher(final EventDispatcher initial) {
                defaultEventDispatcher = initial;
            }
    
            @Override
            public Event dispatchEvent(final Event event, final EventDispatchChain tail) {
                final EventType<? extends Event> type = event.getEventType();
                if (type == MouseEvent.MOUSE_CLICKED) {
                    final MouseEvent mouseEvent = (MouseEvent) event;
                    final EventTarget eventTarget = event.getTarget();
                    if (mouseEvent.getClickCount() > 1) {
                        if (clickedTimeline != null) {
                            clickedTimeline.stop();
                            clickedTimeline = null;
                            final MouseEvent dblClickedEvent = copy(mouseEvent, CustomMouseEvent.MOUSE_DOUBLE_CLICKED);
                            Event.fireEvent(eventTarget, dblClickedEvent);
                        }
                        return mouseEvent;
                    }
                    if (clickedTimeline == null) {
                        final MouseEvent clickedEvent = copy(mouseEvent, mouseEvent.getEventType());
                        clickedTimeline = new Timeline(new KeyFrame(Duration.millis(DEFAULT_DOUBLE_CLICK_DELAY), e -> {
                            Event.fireEvent(eventTarget, clickedEvent);
                            clickedTimeline = null;
                        }));
                        clickedTimeline.play();
                        return mouseEvent;
                    }
                }
                return defaultEventDispatcher.dispatchEvent(event, tail);
            }
    
            /**
             * Creates a copy of the provided mouse event type with the mouse event.
             *
             * @param e         MouseEvent
             * @param eventType Event type that need to be created
             * @return New mouse event instance
             */
            private MouseEvent copy(final MouseEvent e, final EventType<? extends MouseEvent> eventType) {
                return new MouseEvent(eventType, e.getSceneX(), e.getSceneY(), e.getScreenX(), e.getScreenY(),
                        e.getButton(), e.getClickCount(), e.isShiftDown(), e.isControlDown(), e.isAltDown(),
                        e.isMetaDown(), e.isPrimaryButtonDown(), e.isMiddleButtonDown(),
                        e.isSecondaryButtonDown(), e.isSynthesized(), e.isPopupTrigger(),
                        e.isStillSincePress(), e.getPickResult());
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 06:47

    An alternative to single click vs. double click that I'm using is single click vs. press-and-hold (for about a quarter to a half second or so), then release the button. The technique can use a threaded abortable timer as in some of the code snippets above to distinguish between the two. Assuming that the actual event handling happens on the button release, this alternative has the advantage that single click works normally (i.e., without any delay), and for press-and-hold you can give the user some visual feedback when the button has been held long enough to be released (so there's never any ambiguity about which action was performed).

    0 讨论(0)
提交回复
热议问题