首页 > 解决方案 > JavaFX 没有 TouchEvents

问题描述

在我的 (Windows) Surface Go 2 平板电脑上,我无法检索任何(多点触控)TouchEvent。我尝试了几个 Java/FX 版本。即使使用 JavaFX 17(和更早的版本),也不会生成 TouchEvents 事件,只会生成鼠标事件。是否需要某种配置参数?

package fx;

import javafx.application.Application;
import javafx.collections.ListChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TouchEvent;
import javafx.scene.layout.Pane;
import javafx.stage.PopupWindow;
import javafx.stage.Stage;
import javafx.stage.Window;

public class test {

    public static void main(String[] args) {
        System.setProperty("com.sun.javafx.touch", "true");
        System.setProperty("com.sun.javafx.isEmbedded", "true");   
        Application.launch(JFXApp.class, args);
    }

    public static class JFXApp extends Application implements ListChangeListener<Window> {

        @Override
        public void start(Stage primaryStage) {
//            primaryStage.addEventFilter(TouchEvent.ANY, e -> System.out.println("touch event: " + e.getEventType()));
//            primaryStage.addEventFilter(MouseEvent.ANY, e -> System.out.println("mouse event: " + e.getEventType()));
            final ComboBox<String> comboBox = new ComboBox<>();
            comboBox.getItems().addAll("Test1", "Test2", "Test3");
            Scene scene = new Scene(new Pane(comboBox));
            scene.addEventFilter(TouchEvent.ANY, e -> System.out.println("scene touch event: " + e.getEventType()));
            scene.addEventFilter(MouseEvent.ANY, e -> System.out.println("scene mouse event: " + e.getEventType()));
            primaryStage.setScene(scene);
            primaryStage.setWidth(800);
            primaryStage.setHeight(800);
            primaryStage.show();
            Window.getWindows().addListener(this);
        }

        @Override
        public void onChanged(Change<? extends Window> c) {
            if (!c.next()) return;
            for (Window w : c.getAddedSubList()) {
                if (w instanceof PopupWindow) {
                    w.addEventFilter(TouchEvent.ANY,
                            e -> System.out.println("touch event (PopupWindow): " + e.getEventType()));
                    w.addEventFilter(MouseEvent.ANY,
                            e -> System.out.println("mouse event (PopupWindow): " + e.getEventType()));
                    Window.getWindows().removeListener(this);
                }
            }
        }
    }
}

标签: javajavafxtabletmulti-touch

解决方案


我使用运行 Windows 的 Microsoft SurfaceBook 2 笔记本电脑在 Java/JavaFX 16 上获得 TouchEvents。

样品来源

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.TouchEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class TouchTest extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        System.out.println("java.version: " + System.getProperty("java.version"));
        System.out.println("javafx.runtime.version: " + System.getProperties().get("javafx.runtime.version"));

        Scene scene = new Scene(new Pane(), 800, 800);
        scene.addEventFilter(TouchEvent.ANY, System.out::println);

        stage.setScene(scene);
        stage.show();
    }
}

执行说明

使用 Java 16 和 JavaFX 16,运行示例程序并触摸显示的空白窗格(通过用手指触摸触摸屏,而不是使用触控板并按下它)。

程序输出

C:\Users\send2\.jdks\temurin-16.0.2\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.2\lib\idea_rt.jar=56576:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.3.2\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\send2\.m2\repository\org\openjfx\javafx-controls\16\javafx-controls-16.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-graphics\16\javafx-graphics-16.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-base\16\javafx-base-16.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-fxml\16\javafx-fxml-16.jar -p C:\Users\send2\.m2\repository\org\openjfx\javafx-base\16\javafx-base-16-win.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-graphics\16\javafx-graphics-16-win.jar;C:\dev\fxdemo\target\classes;C:\Users\send2\.m2\repository\org\openjfx\javafx-controls\16\javafx-controls-16-win.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-fxml\16\javafx-fxml-16-win.jar -m org.jewelsea.fxdemo/org.jewelsea.fxdemo.TouchTest
java.version: 16.0.2
javafx.runtime.version: 16+8
TouchEvent [source = javafx.scene.Scene@3968dc9e, target = Pane@295c8184[styleClass=root], eventType = TOUCH_PRESSED, consumed = false, touchCount = 1, eventSetId = 1, touchPoint = TouchPoint [state = PRESSED, id = 1, target = Pane@295c8184[styleClass=root], x = 394.5, y = 330.5, z = 0.0, pickResult = PickResult [node = Pane@295c8184[styleClass=root], point = Point3D [x = 394.5, y = 330.5, z = 0.0], distance = 1492.820323027551]]
TouchEvent [source = javafx.scene.Scene@3968dc9e, target = Pane@295c8184[styleClass=root], eventType = TOUCH_MOVED, consumed = false, touchCount = 1, eventSetId = 2, touchPoint = TouchPoint [state = MOVED, id = 1, target = Pane@295c8184[styleClass=root], x = 394.0, y = 331.0, z = 0.0, pickResult = PickResult [node = Pane@295c8184[styleClass=root], point = Point3D [x = 394.0, y = 331.0, z = 0.0], distance = 1492.820323027551]]
TouchEvent [source = javafx.scene.Scene@3968dc9e, target = Pane@295c8184[styleClass=root], eventType = TOUCH_STATIONARY, consumed = false, touchCount = 1, eventSetId = 3, touchPoint = TouchPoint [state = STATIONARY, id = 1, target = Pane@295c8184[styleClass=root], x = 394.0, y = 331.0, z = 0.0, pickResult = PickResult [node = Pane@295c8184[styleClass=root], point = Point3D [x = 394.0, y = 331.0, z = 0.0], distance = 1492.820323027551]]
TouchEvent [source = javafx.scene.Scene@3968dc9e, target = Pane@295c8184[styleClass=root], eventType = TOUCH_STATIONARY, consumed = false, touchCount = 1, eventSetId = 4, touchPoint = TouchPoint [state = STATIONARY, id = 1, target = Pane@295c8184[styleClass=root], x = 394.0, y = 331.0, z = 0.0, pickResult = PickResult [node = Pane@295c8184[styleClass=root], point = Point3D [x = 394.0, y = 331.0, z = 0.0], distance = 1492.820323027551]]
TouchEvent [source = javafx.scene.Scene@3968dc9e, target = Pane@295c8184[styleClass=root], eventType = TOUCH_RELEASED, consumed = false, touchCount = 1, eventSetId = 5, touchPoint = TouchPoint [state = RELEASED, id = 1, target = Pane@295c8184[styleClass=root], x = 394.0, y = 331.0, z = 0.0, pickResult = PickResult [node = Pane@295c8184[styleClass=root], point = Point3D [x = 394.0, y = 331.0, z = 0.0], distance = 1492.820323027551]]

执行行仅供参考,当我使用其内部运行功能启动应用程序时,它是由我的 IDE (Intellij Idea) 自动生成的。我确信它可以在 IDE 之外使用不同的执行命令正常工作,但使用相同的 JRE/JavaFX 版本。

示例适用于 JavaFX 16,而不是 17.0.0.1

如果您使用当前版本的 JavaFX 17,在我设置的运行 Windows 的 Surface Book 2 上,上面的示例将不会接收触摸事件(它们被映射到鼠标事件)。何塞对这个问题的评论对此进行了解释:

在 JavaFX 16 发布后,此问题JDK-8249737已得到修复。它改变了在 Windows 上处理触摸事件的方式:并非所有事件都是直接事件。在此修复之前,触摸事件报告给场景,但现在间接事件退出:链接,并被视为鼠标事件。

这也(主要)适用于此处找到的旧 Oracle TouchEvent 教程示例代码:

该代码的多点触控文件夹拖动部分适用于我的 Java 16 设置。

将球拖到该代码的框部分对我来说在 Java 16 上不起作用。它似乎记录了最初的触摸,但我无法计算出将球带到另一个盒子的触摸或触摸和拖动顺序,所以它似乎坏了。

对于 Java 17,触摸事件示例中没有任何内容响应触摸事件(或鼠标事件)。

常问问题

对于 MacOS,如果 Trackpad 事件也生成触摸事件,那就太好了。

使用 Mac 触控板不会生成触摸事件(在 JavaFX 17 上测试)。与 windows 触摸处理不同,我不认为这个 Mac 代码是为 JavaFX 17 更新的,所以它可能一直都是这样。我没有触摸屏来尝试一下。

对于 iOS 实现,它通过检查当您触摸 iOS 设备屏幕时会生成触摸事件的代码(但我没有设置来运行 JavaFX iOS 来测试它)。

似乎有多个问题。

是的我同意。

我还认为,其中一些问题是特定于平台和设备的,最终可能会有些细微差别,而且并不完全直截了当。

我提供的信息几乎是我对这个主题的知识的限制,所以很遗憾,我无法提供更多帮助。

当我使用 bell-sw 或 Azul Full-JDK 16 对其进行测试时,它不起作用

它适用于 gluon JavaFX 16 发行版,但不适用于 azul 或 bell-sw 捆绑发行版,这有点奇怪,因为人们会认为它们具有相同的功能,但这几乎可以说是这样。

我猜想可以联系那些捆绑的 JDK/JavaFX 发行版的制造商,或者为此向他们提交问题报告。但是,随着 Java 17 的功能发生变化,我不确定它是否会在未来产生很大的不同,至少目前是这样。

当我使用 Oracle Java 17 并使用从 gluonhq.com/products/javafx 下载的 JavaFX 16 时,它确实有效!但是 JavaFX 17 在任何情况下都不起作用。

是的,这与我使用来自 Maven 中心的模块化 JavaFX 版本得到的结果相同。

还是必须以不同的方式检索触摸事件?

我认为除了破解 JavaFX 核心之外,没有其他方法可以检索它们。该实现在 JavaFX 库代码中,不能通过公共 API 进行配置。

对于某些平台(例如 iOS),看起来 TouchEvents 是通过标准事件过滤器生成和路由的。

但是对于其他平台,那里有特定的代码可以在某些情况下忽略 TouchEvents。至少对于 17.0.0.1 版本,当在 JavaFX 框架中检测到 TouchEvents 时,会故意忽略它。当您的手指触摸 Windows 操作系统设备上的触摸屏时,它们不会路由到用户 JavaFX 应用程序代码。请注意,在这种情况下,触摸被视为 MouseEvents 并相应地采取行动 (AFAICT)。

因此,您需要自定义和更改核心 JavaFX 代码以在您的 Windows 操作系统平台上获取触摸事件,方法是自己破解 JavaFX 库代码,或者将更改集成到库中以供将来的 JavaFX 版本使用。

对 JavaFX TouchEvent 实现的思考(以及改变它)

也许这个问题已经被彻底考虑过了,JavaFX 17.0.0.1 中的平台代码正在按设计工作。或许想法是,当前的行为,虽然不理想,但也是一种合理的妥协。也许结果可能是文档更新以更好地描述 TouchEvent 行为,而不是功能更新来更改它。

但是 IMO,从目前的行为来看,当 JavaFX 中的 TouchEvent 处理功能在某些平台(例如 Windows 操作系统)上运行时,它的设计和实现似乎存在问题。我不知道如何对这些问题进行分类或解决。

如果您希望在这方面与 JavaFX 开发人员合作,我建议您加入openjfx-dev邮件列表并在那里描述问题(链接回这个问题)并开始讨论可以做些什么。


推荐阅读