how to differentiate between a single click or double click on a table row in javafx

前端 未结 2 585
一向
一向 2021-01-26 11:44

I am trying to create a table in javafx that allows a user to click on a row to go to one page or double click the row to go to a different page. The problem is that the applica

相关标签:
2条回答
  • 2021-01-26 12:26

    There's no way to do this that's part of the API: you just have to code "have the program wait and see if there is another click" yourself. Note that this means that the single-click action has to have a slight pause before it's executed; there's no way around this (your program can't know what's going to happen in the future). You might consider a different approach (e.g. left button versus right button) to avoid this slightly inconvenient user experience.

    However, a solution could look something like this:

    public class DoubleClickHandler {
    
        private final PauseTransition delay ;
        private final Runnable onSingleClick ;
        private final Runnable onDoubleClick ;
        private boolean alreadyClickedOnce ;
    
        public DoubleClickHandler(
            Duration maxTimeBetweenClicks,
            Runnable onSingleClick,
            Runnable onDoubleClick) {
    
            alreadyClickedOnce = false ;
            this.onSingleClick = onSingleClick ;
            this.onDoubleClick = onDoubleClick ;
    
            delay = new PauseTransition(maxTimeBetweenClicks);
            delay.setOnFinished(e -> {
                alreadyClickedOnce = false ;
                onSingleClick.run()
            });
        }
    
        public void applyToNode(Node node) {
            node.setOnMouseClicked(e -> {
                delay.stop();
                if (alreadyClickedOnce) {
                    alreadyClickedOnce = false ;
                    onDoubleClick.run();
                } else {
                    alreadyClickedOnce = true ;
                    delay.playFromStart();
                }
            });
        }
    }
    

    Which you can use with:

    searchResults.setRowFactory(tv -> {
        TableRow<MovieRow> row = new TableRow<>();
        DoubleClickHandler handler = new DoubleClickHandler(
            Duration.millis(500),
            () -> {
                MovieRow tempResult = row.getItem();
                System.out.println(tempResult.getMTitle + " was clicked once");
            },
            () -> {
                MovieRow tempResult = row.getItem();
                System.out.println(tempResult.getMTitle + " was clicked twice");
            }
        );
        handler.applyToNode(row);
        return row ;
    });
    
    0 讨论(0)
  • 2021-01-26 12:29

    I encountered the same requirement once and worked on developing a custom event dispatcher. The solution what @James_D provided is clean, simple and works great. But if you want to generalize this behavior on a large scale, you can define a new custom mouse event and an event dispatcher.

    The advantage of this approach is its usage will be just like other mouse events and can be handled in both event filters and handlers.

    Please check the below demo and the appropriate code:

    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();
    
            // THIS IS THE PART OF CODE SETTING CUSTOM EVENT DISPATCHER
            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)
提交回复
热议问题