JavaFX 没有 TouchEvents

JavaFX no TouchEvents

在我的 (Windows) Surface Go 2 平板电脑上,我无法检索任何(多点触控)TouchEvents。我尝试了几个 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);
                }
            }
        }
    }
}

我不是 JavaFX 开发人员。但是,我在 Android 应用程序上实现 TouchEvent 时遇到了这样的问题。

我认为 TouchEvent 没有发送到您的 EventListener。因为有一个顶层抓取事件并且不会将其分派到您的 EventLister 所在的较低层。

我解决了这个可能的问题,我推荐的解决方案是为 ComboBoxsetEventDispatcher.

实现一个新的事件调度程序

Property description:

Specifies the event dispatcher for this node. The default event dispatcher sends the received events to the registered event handlers and filters. When replacing the value with a new EventDispatcher, the new dispatcher should forward events to the replaced dispatcher to maintain the node's default event handling behavior.

我在 Java/JavaFX 16 上使用 Microsoft SurfaceBook 2 笔记本电脑 运行ning Windows.

获得 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();
    }
}

执行指令

使用 Java16 和 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\javafx-controls-16.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-graphics\javafx-graphics-16.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-base\javafx-base-16.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-fxml\javafx-fxml-16.jar -p C:\Users\send2\.m2\repository\org\openjfx\javafx-base\javafx-base-16-win.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-graphics\javafx-graphics-16-win.jar;C:\dev\fxdemo\target\classes;C:\Users\send2\.m2\repository\org\openjfx\javafx-controls\javafx-controls-16-win.jar;C:\Users\send2\.m2\repository\org\openjfx\javafx-fxml\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,在我设置的 Surface Book 2 运行 上,上面的示例将不会接收触摸事件(它们被映射到鼠标事件)宁Windows。何塞对这个问题的评论对此进行了解释:

Right after the release of JavaFX 16, this issue JDK-8249737 was fixed. It changed the way touch events were treated on Windows: not all events are direct events. Before this fix, touch events reported to scene, but now indirect events bail out: link, and treated as mouse events.

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

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

设置

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

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

常见问题解答

for MacOS it would be nice that the Trackpad event also generate touch events.

使用 Mac 触控板不会生成触摸事件(在 JavaFX 17 上测试)。与 windows 触摸处理不同,我认为此 Mac 代码并未针对 JavaFX 17 进行更新,因此它可能一直都是这样。我没有触摸屏,无法尝试。

对于 iOS 实现,从检查代码可以看出,当您触摸 iOS 设备的屏幕时它会生成触摸事件(但我还没有设置 运行 JavaFX iOS 进行测试)。

It seems that there are multiple issues.

是的,我同意。

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

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

When I test it with bell-sw or Azul Full-JDK 16, it doesn't work

有点奇怪,它适用于 gluon JavaFX 16 发行版,但不适用于 azul 或 bell-sw 捆绑发行版,因为人们会认为它们具有相同的功能,但仅此而已尽我所能。

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

When I use Oracle Java 17 and use the JavaFX 16 download from gluonhq.com/products/javafx it does work! But JavaFX 17 doesn't work in any case.

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

or must the touchevents be retrieved in a different way?

我认为除了破解 JavaFX 核心之外,没有其他方法可以检索它们。实现在 JavaFX 库代码中,无法通过 public API.

配置

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

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

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

关于 JavaFX TouchEvent 实现(及其更改)的想法

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

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

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