JavaFX 应用程序,将数据从 nativeMouseListener 中继到 JavaFX 的框架

JavaFX application, framework to relay data from a nativeMouseListener to JavaFX

我正在制作一个 JavaFX 应用程序,我想执行以下操作。

我提供了几个按钮,按下它们时,GUI 将最小化并等待来自 JNativeHook 库的 mouseClick。单击鼠标时,它的 x、y 坐标需要传回 javaFx 控制器,GUI 最大化并表示 gui 中的数据,让我可以用它做其他事情。

我的这个行为对一个按钮有效,但一点也不好。如果我想要更多按钮,我当前的实现将无法工作,如下所示,它需要大量重复代码,我需要在布局控制器中为每个按钮(除了 buttonHandlers 之外)制作一个特定的 mouseListener 和方法。我显然不想要这个,基本上我正在寻找一个小框架,让下一个 NativeMouseEvent 在我的控制器中的 buttonHandle 方法中中继数据

这就是我得到的,带有按钮的 JavaFX layoutController,具有以下句柄

@FXML
private void handleGetPos() {
    try {
        mouseManager.startMousePosChecker(cntrl);
        mainGUI.minimizeGUI();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

layoutController 将自身作为 cntrl 传递给我的 mouseManager,管理器只是(取消)注册一个 nativeHook 到 GlobalScreen 并向其添加一个新的 mouseListener 并传递 layoutController cntrl用它。 (如果我想使用另一个布局控制器,将控制器传递给它也是愚蠢的,因为它们不是同一类型)

    GlobalMouseListener mouseListener = new GlobalMouseListener();
    mouseListener.setLayoutController(cntrl);                       
    GlobalScreen.getInstance().addNativeMouseListener(mouseListener);

在 mouseListener 中发生了这种情况:

public void nativeMouseClicked(NativeMouseEvent e) {
    Platform.runLater(() -> {
        cntrl.setMousePos(ConvTypes.toStr(e.getX()), ConvTypes.toStr(e.getY()), e.getX(), e.getY() );
    });
}

这很丑陋,当事件发生时,它会将 x、y 坐标作为字符串传递给我的布局控制器中的特定方法:

public void setMousePos(String sX, String sY, int x, int y) {
    xPos.setText(sX);
    yPos.setText(sY);
    mainGUI.maximizeGUI();
    mouseManager.stopMousePosChecker();
    //other stuff

显然,此实现不适用于多个按钮。因此我想用 mouseManager 构建一个小框架。其中 registers/unregisters nativeHook 和仅制作一个 mouseListener,其中 returns 数据到 mouseManager。然后这个 mouseManager 将必要的数据提供给任何需要它的人。如果我是对的,这听起来像是一个自定义事件 class? (试图制作一个但我不知道如何实现我的 layoutController 或如何将它包装在 globalMouseEvents 周围)

无论如何我认为它会变成这样或者可能是带有 lambda 的东西? (注意我其实不知道):

     @FXML
     private void handleButton() {
         xPos.setText(mouseManager<MouseEvent>() {
         @Override
         public void handle(MouseEvent e) {
              //necessery stuff goes here

所以是的,我不知道如何开始如何在任何 buttonHandle() 方法或任何实际方法中将数据获取到 return。我知道我当前的代码很糟糕,我想让它变得更好并重用代码而不是重复代码,但这个问题目前让我很头疼...

很长post,感谢阅读和帮助!

不确定我是否完全理解,但是你可以这样做吗:

import org.jnativehook.mouse.NativeMouseEvent;
import org.jnativehook.mouse.NativeMouseListener;
import org.jnativehook.GlobalScreen;


import javafx.scene.input.MouseEvent ;
import javafx.application.Platform ;
import javafx.event.EventType ;
import javafx.geometry.Point2D ;
import javafx.beans.property.ReadOnlyObjectWrapper ;
import javafx.beans.property.ReadOnlyObjectProperty ;

public class GlobalMouseListener implements NativeMouseListener {
    private final EventType<MouseEvent> eventType ;
    private final ReadOnlyObjectWrapper<Point2D> mouseLocation = new ReadOnlyObjectWrapper<>();

    /**
      * @param eventType The type of event to listen for. Should be one of
      * MouseEvent.MOUSE_CLICKED, MouseEvent.MOUSE_PRESSED, or MouseEvent.MOUSE_RELEASED.
      * The mouseLocationProperty() will be updated if a global mouse event
      * matching the semantic type of the provided eventType occurs.
      */

    public GlobalMouseListener(EventType<MouseEvent> eventType) {
        this.eventType = eventType ;
        GlobalScreen.addNativeMouseListener(this);
    }

    public ReadOnlyObjectProperty<Point2D> mouseLocationProperty() {
        return mouseLocation.getReadOnlyProperty() ;
    }

    public final Point2D getMouseLocation() {
        return mouseLocationProperty().get();
    }

    @Override
    public void nativeMouseClicked(NativeMouseEvent e) {
        if (eventType == MouseEvent.MOUSE_CLICKED) {
            Platform.runLater(() -> mouseLocation.set(new Point2D(e.getX(), e.getY())));
        }
    }

    @Override
    public void nativeMousePressed(NativeMouseEvent e) {
        if (eventType == MouseEvent.MOUSE_PRESSED) {
            Platform.runLater(() -> mouseLocation.set(new Point2D(e.getX(), e.getY())));
        }
    }    

    @Override
    public void nativeMouseReleased(NativeMouseEvent e) {
        if (eventType == MouseEvent.MOUSE_RELEASED) {
            Platform.runLater(() -> mouseLocation.set(new Point2D(e.getX(), e.getY())));
        }
    }

    public void cleanup() {
        GlobalScreen.removeNativeMouseListener(this);
    }
}

那么你的按钮处理程序只需要

@FXML
public void handleGetPos() {
    GlobalMouseListener gml = new GlobalMouseListener(MouseEvent.MOUSE_CLICKED);
    gml.mouseLocationProperty().addListener((obs, oldLocation, newLocation) -> {
        double x = newLocation.getX();
        double y = newLocation.getY();
        gml.cleanup();
        // restore window and process x, y...
    });
    mainGUI.minimizeGUI();
}

如果你真的想最小化样板代码,你可以定义

private void processMouseLocation(BiConsumer<Double, Double> pointProcessor) {
    GlobalMouseListener gml = new GlobalMouseListener(MouseEvent.MOUSE_CLICKED);
    gml.mouseLocationProperty().addListener((obs, oldLocation, newLocation) -> {
        double x = newLocation.getX();
        double y = newLocation.getY();
        gml.cleanup();
        // restore window...
        pointProcessor.accept(x, y);
    });
    mainGUI.minimizeGUI();
}

然后您的每个按钮处理程序都减少到

@FXML
public void handleGetPos() {
    processMouseLocation((x, y) -> {
        // code to handle mouse press at x,y...
    });
}

这些都是未经测试的(包括编译和 运行,我只是在此处输入),但我认为这应该足以让您入门。重点是您只需为鼠标位置公开一个 属性 ,并从每个按钮处理程序中收听它,并在它发生变化时执行特定于按钮的代码。当相关事件发生时,您的本地鼠标侦听器只需更新 属性;它不需要对控制器等的引用。