如何在javafx中区分对表行的单击和双击

如何在javafx中区分对表行的单击和双击,javafx,click,double-click,tablerow,Javafx,Click,Double Click,Tablerow,我试图在javafx中创建一个表,允许用户单击一行转到一个页面,或者双击该行转到另一个页面。问题是应用程序注册了一次单击事件,但并不等待查看是否有另一次双击。有没有办法让程序等待,看看是否有另一次点击 到目前为止,我所拥有的看起来类似于 TableView searchResults; ObservableList<MovieRow> rows = FXCollections.observableArrayList(); private TableColumn<MovieRo

我试图在javafx中创建一个表,允许用户单击一行转到一个页面,或者双击该行转到另一个页面。问题是应用程序注册了一次单击事件,但并不等待查看是否有另一次双击。有没有办法让程序等待,看看是否有另一次点击

到目前为止,我所拥有的看起来类似于

TableView searchResults;
ObservableList<MovieRow> rows = FXCollections.observableArrayList();

private TableColumn<MovieRow, String> title;
title.setCellValueFactory(new PropertyValueFactory<>("mTitle"));

rows.add(new MovieRow("The cat in the hat"));

searchResults.setItems(rows);

searchResults.setRowFactory(tv -> {
    TableRow<MovieRow> row = new TableRow<>();
    row.setOnMouseClicked(event -> {
        MovieRow tempResult = row.getItem();

        if (event.getClickCount() == 1) {
            System.out.println(tempResult.getMTitle + " was clicked once");
        }else{
            System.out.println(tempResult.getMTitle + " was clicked twice");
        }
    });
    return row;
});

public class MovieRow{
    private String mTitle;
    
    public MovieRow(String title){
           mTitle = title;
    }

    public String getMTitle() {
            return mTitle;
    }
}
期望输出

single click: The cat in the hat was clicked once
double click: The cat in the hat was clicked twice

我只发现了自己处理双击或单键点击的结果,但两者都没有,所以我不确定这是否可行。任何帮助都将不胜感激。谢谢。

这是API的一部分,没有办法做到这一点:您只需自己编写“让程序等待,看看是否有另一次单击”。请注意,这意味着单击操作在执行之前必须稍微暂停;这是无法避免的(你的程序不知道将来会发生什么)。你可以考虑一种不同的方法(例如左按钮和右按钮)来避免这种稍微不方便的用户体验。

但是,解决方案可能如下所示:

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();
            }
        });
    }
}
您可以将其用于:

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 ;
});
searchResults.setRowFactory(电视->{
TableRow行=新TableRow();
DoubleClickHandler=新的DoubleClickHandler(
持续时间。毫秒(500),
() -> {
MovieRow tempResult=row.getItem();
System.out.println(tempResult.getMTitle+“单击一次”);
},
() -> {
MovieRow tempResult=row.getItem();
System.out.println(tempResult.getMTitle+“被点击两次”);
}
);
handler.applyToNode(行);
返回行;
});

我曾经遇到过同样的需求,并致力于开发一个自定义事件调度器。@James_D提供的解决方案干净、简单,效果很好。但是,如果您想大规模地推广这种行为,可以定义一个新的自定义鼠标事件和事件调度器

这种方法的优点是它的用法与其他鼠标事件一样,可以在事件过滤器和处理程序中处理

请检查以下演示和相应的代码:

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());
        }
    }
}
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.event.*;
导入javafx.geometry.Pos;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.scene.input.MouseEvent;
导入javafx.scene.layout.HBox;
导入javafx.scene.layout.StackPane;
导入javafx.scene.shape.Rectangle;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类DoubleClickEventDispatcherDemo扩展应用程序{
@凌驾
public void start(Stage)引发异常{
矩形框1=新矩形(150150);
框1.设置样式(“-fx填充:红色;-fx笔划宽度:2px;-fx笔划:黑色;”);
加法器(方框1,“红色方框”);
矩形框2=新矩形(150150);
框2.设置样式(“-fx填充:黄色;-fx笔划宽度:2px;-fx笔划:黑色;”);
加法器(第2框,“黄色框”);
HBox窗格=新的HBox(框1,框2);
窗格。设置间距(10);
窗格设置对齐(位置中心);
增补的附录(窗格,“HBox”);
场景=新场景(新堆栈窗格(窗格),450,300);
舞台场景;
stage.show();
//这是设置自定义事件调度程序的代码部分
scene.setEventDispatcher(新的双击EventDispatcher(scene.getEventDispatcher());
}
专用void addEventHandler(节点节点,字符串节点ID){
node.addEventFilter(鼠标点击MouseEvent.MOUSE_,e->System.out.println(“+nodeId+”鼠标点击过滤器”);
node.addEventHandler(MouseEvent.MOUSE_单击,e->System.out.println(“+nodeId+”鼠标单击的处理程序”);
node.addEventFilter(CustomMouseeEvent.MOUSE_双击,e->System.out.println(“+nodeId+”鼠标双击过滤器”);
node.addEventHandler(CustomMouseeEvent.MOUSE_双击,e->System.out.println(nodeId+“鼠标双击处理程序”);
}
/**
*自定义鼠标事件
*/
接口CustomMouseEvent{
EventType MOUSE\u DOUBLE\u CLICKED=新的事件类型(MouseEvent.ANY,“MOUSE\u DBL\u CLICKED”);
}
/**
*自定义EventDispatcher以区别于单击和双击。
*/
类DoubleClickEventDispatcher实现EventDispatcher{
/**
*触发双击事件的默认延迟(毫秒)。
*/
私有静态最终长默认值\u双击\u延迟=215;
/**
*节点的默认事件调度程序。
*/
私有最终事件调度器defaultEventDispatcher;
/**
*调度鼠标单击事件的时间线。
*/
私人时间线点击时间线;
/**
*构造器。
*
*@param节点的初始默认事件调度程序
*/
公共双击事件调度程序(最终事件调度程序初始){
defaultEventDispatcher=初始;
}
@凌驾
公共事件调度事件(最终事件事件、最终事件调度链尾){

最终事件类型您单击一行转到一页的口头描述或双击该行转到另一页的口头描述与所需的输出相矛盾(我理解为您既要单次输出,也要双次输出,这是默认行为)-请澄清任何混淆。我有一行代码调用其他类来更改当前正在工作的页面,因此我想特别关注表行中单击与双击的最简单示例。我认为简单的打印语句是最小的可复制示例。不,不是-请参考单击“帮助”页面并相应地执行:)无论如何,
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());
        }
    }
}