分离触摸和鼠标事件

3

我使用触摸屏幕来控制应用程序中的节点。但是如果应用程序没有运行触摸屏幕时,我也希望使用鼠标事件来控制这些节点,以便可以处理两种事件。我尝试了以下方法:

private void initDraggable() {
    touchFilter = new EventHandler<TouchEvent>() {
        @Override public void handle(TouchEvent event) {
            System.out.println("Touch dragged");    

            touchTaskViewModel.actualClickPointProperty().set(new ClickPoint(event.getTouchPoint().getX(), event.getTouchPoint().getY()));
            touchTaskViewModel.sceneXProperty().set(event.getTouchPoint().getSceneX());

            touchTaskViewModel.handleTaskMoved();

            event.consume();
        }
    };

    addEventFilter(TouchEvent.TOUCH_MOVED, touchFilter);

    setOnTouchPressed(new EventHandler<TouchEvent>() {
        @Override
        public void handle(TouchEvent event) {
            touchTaskViewModel.entryClickPointProperty().set(new ClickPoint(event.getTouchPoint().getX(), event.getTouchPoint().getY()));

            touchTaskViewModel.taskSelectedProperty().set(true);

            event.consume();
        }
    });

    setOnTouchReleased(new EventHandler<TouchEvent>() {
        @Override
        public void handle(TouchEvent event) {
            touchTaskViewModel.taskSelectedProperty().set(false);

            event.consume();
        }
    });

    clickFilter = new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent event) {
          if(!event.isSynthesized()){
            System.out.println("Mouse dragged!");   

            touchTaskViewModel.actualClickPointProperty().set(new ClickPoint(event.getX(), event.getY()));
            touchTaskViewModel.sceneXProperty().set(event.getSceneX());

            touchTaskViewModel.handleTaskMoved();
            event.consume();
          }
      }
    };

    addEventFilter(MouseEvent.MOUSE_DRAGGED, clickFilter);

    setOnMousePressed(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            if(!event.isSynthesized()){

                touchTaskViewModel.entryClickPointProperty().set(new ClickPoint(event.getX(), event.getY()));

                touchTaskViewModel.taskSelectedProperty().set(true);

                event.consume();
            }
        }
    });

    setOnMouseReleased(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            if(!event.isSynthesized()){ 
                touchTaskViewModel.taskSelectedProperty().set(false);

                event.consume();
            }
        }
    });
}

所以,如果事件已经由触摸事件处理,则函数isSynthesized()应返回true。但是,无论我使用触摸监视器还是鼠标拖动节点,它始终返回false。
触摸事件已经被正确处理,我看到它打印了“触摸拖动”和“鼠标拖动”。

是的,isSynthesized函数不太好用。没有简单的解决方案 :( - Xdg
1个回答

4
混合触摸和鼠标事件很难。即使isSynthesized可靠(在我看来总是这样),它也无法解决所有可能的问题。例如,当你有一个Button时,你希望能够通过鼠标或触摸屏幕触发它的操作,你可以让触摸屏幕模拟鼠标,这将不需要额外的代码就能像处理鼠标界面一样正常工作。然而,当你使用鼠标单击时,Button会获取焦点,并在按住左键时变为武装状态。但是,Button的动作直到释放左键才会触发,这是与触摸屏幕不同的表现。
对于触摸界面,鼠标模拟效果不太好。当您用手指按下Button时,它不会获得焦点,也不会变成武装状态。当您通过从屏幕上移开手指释放Button时,您会获得鼠标按下事件,然后动作被触发,接着然后(有些难以解释)您会得到鼠标释放和鼠标单击事件。因此,当您依赖于触摸屏幕的鼠标模拟时,视觉反馈不太好。此外,根据您的喜好,通过屏幕推按钮不会像真正的机电按钮那样在按下时引发它们的动作而不是在释放时引发。
现在,假设您希望鼠标界面继续以原来的方式工作(即在鼠标释放事件上触发操作),您可以将自己的“跳过”标志添加到触摸事件处理中,并使动作处理程序查询该标志。技巧在于知道何时设置该标志和何时不设置该标志。人们希望在处理触摸按压事件时总是设置该标志,因为这是将获取焦点、武装Button并触发动作的事件,所以您不希望鼠标事件再次触发动作。
但是...
如果在从屏幕中抬起前将手指从Button上拖开,就不会再次触发。这是因为模拟的鼠标释放事件不在Button内部,真正的鼠标也不会触发它的动作,因此模拟也不会触发动作(尽管鼠标释放和鼠标点击事件在动作处理程序调用之后都会发送到您的代码中)。
因此,为了避免在不会再次触发动作时设置跳过标志,必须不在触摸按压处理程序中设置它。只有当释放发生在包含在Button内的屏幕点上时,才应该在触摸释放处理程序中有条件地设置它。
下面的代码展示了如何实现拖动功能。(我知道你问的是拖动而不是按钮,但你已经等了两年没有得到答案,我认为处理触摸界面和鼠标界面之间协作的问题,特别是需要确保触摸事件不会导致动作双重触发的问题,可以通过类似我在这里使用的技术来通常解决。希望能有所帮助。)
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.TouchEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ShowNanoTime extends Application
{
    Label msg = new Label();

    public static void main(String... args)
    {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception
    {
        Button btnShowTime = new Button("Show Time");
        VBox root = new VBox();

        Handler handler = new Handler();

        btnShowTime.setOnTouchPressed(handler.new TouchPressed());
        btnShowTime.setOnTouchReleased(handler.new TouchReleased());

        btnShowTime.setOnAction(handler.new Action(this));

        root.getChildren().addAll(msg, btnShowTime);

        Scene scene = new Scene(root, 640, 360);
        stage.setScene(scene);

        stage.show();
    }

    void showTime()
    {
        msg.setText(String.format("%d", System.nanoTime()));
    }
}

class Handler
{
    boolean skip = false;

    class TouchPressed implements EventHandler<TouchEvent>
    {
        @Override
        public void handle(TouchEvent event)
        {
            System.out.println("Touch pressed.");
            ((Button) event.getSource()).requestFocus();
            ((Button) event.getSource()).arm();
            ((Button) event.getSource()).fire();
        }
    }

    class TouchReleased implements EventHandler<TouchEvent>
    {
        @Override
        public void handle(TouchEvent event)
        {
            System.out.println("Touch released");

            Button button = (Button) event.getSource();

            double x = event.getTouchPoint().getX();
            double y = event.getTouchPoint().getY();

            if (button.contains(x, y))
            {
                System.out.println("Setting skip.");
                skip = true;
            }
        }
    }

    class Action implements EventHandler<ActionEvent>
    {
        ShowNanoTime app;

        Action(ShowNanoTime app)
        {
            this.app = app;
        }

        @Override
        public void handle(ActionEvent event)
        {
            if (skip)
            {
                skip = false;
                System.out.println("Action skipped.");
                return;
            }

            System.out.println("Action happens!");

            app.showTime();
        }
    }
}

注意:我的代码中没有任何特定于平台的内容,但我只在Windows 10机器上运行了上述代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接