为javafx方法实现全局按键

时间:2018-02-23 15:29:38

标签: java javafx java-native-interface overloading keyboard-events

是否可以使用javafx方法实现jnativehook .setOnKeyPressed()?

例如,这是在应用程序聚焦时它如何监听按键操作:

ListView<String> list = new ListView<String>();

list.setOnKeyPressed(new EventHandler<KeyEvent>() {

    @override
    public void handle(KeyEvent event){

        if (event.getCode() == KeyCode.U) {

            //Go Up list..

         }

         event.consume();

     }

});

但是我希望它能够在关注全局按键时关注另一个应用程序,所以理想情况下我希望它看起来像这样:

list.setOnKeyPressed(new EventHandler<NativeKeyEvent>() {

    @override
    public void handle(NativeKeyEvent event){

        if (event.getKeyCode() == NativeKeyEvent.VC_U) {

            //Go Up list..

         }

         event.consume();

     }

});

但是第一行标记了错误:

1. The method setOnKeyPressed(EventHandler <? super KeyEvent>) in the type Node is not applicable for the arguments (new EventHandler<NativeKeyEvent>(){})

2. Bound mismatch: The type NativeKeyEvent is not a valid substitute for the bounded parameter <T extends Event> of the type EventHandler<T>

我确定我需要重载方法以考虑这些参数,但是我不知道如何处理它以及如何处理第二个错误。

对于jnativehook,方法.consume()也是unsupported

或者是否有另一种方法可以在不使用这些方法的情况下在javafx中上下查看listview并与jnativehook兼容?

1 个答案:

答案 0 :(得分:0)

首先,让我们谈谈事件传递和UI线程,因为您必须了解在线程和内存之间共享的情况下进行的上下文切换。

几乎所有其他应用程序设计的Java都使用单个线程来处理所有UI事件。其原因是well documented遍布互联网。因此,如果您使用的是AWT / Swing或Java FX,您的事件将分别在AWT / Swing或Java FX平台线程上发送,并且没有其他线程应该进行交互或期望这些事件。

JNativeHook使用自己的调度线程,完全独立于Java的UI。这主要是出于兼容性原因而完成的,因为我不想在实现用户上强制使用特定的UI API。因此,如果您想使用AWT / Swing,Java FX或其他东西,则必须在负责UI的线程上下文中调度本机事件。库中内置了一个Spring调度程序,我发布了here的Java FX调度程序,通过默认的调度方法,通过向GlobalScreen注册调度程序,可以进行非常简单的上下文切换。

你可能会问自己,&#34;如果我想在调度之前修改事件该怎么办?&#34;这也应该是可能的,但需要更多努力来注册调度类。您应该能够扩展GlobalScreen类及其匿名内部类,以完成您需要的任何其他内容。例如,如果要调度Swing InputEvents而不是NativeInputEvents,则可以执行以下操作:

public class CustomScreen extends GlobalScreen {

    // Static blocks are run when the class is loaded.
    static {
        // Add adaptors to the listener to convert the events.
        addNativeKeyListener(new KeyAdapter());
        addNativeMouseListener(new MouseAdapter());

        setEventDispatcher(new SwingDispatchService());
    }

    private static class KeyAdapter extends SwingKeyAdapter {
        public void keyTyped(KeyEvent keyEvent) {
            KeyListener[] listeners = eventListeners.getListeners(KeyListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].keyTyped(keyEvent);
            }
        }

        public void keyPressed(KeyEvent keyEvent) {
            KeyListener[] listeners = eventListeners.getListeners(KeyListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].keyPressed(keyEvent);
            }
        }

        public void keyReleased(KeyEvent keyEvent) {
            KeyListener[] listeners = eventListeners.getListeners(KeyListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].keyReleased(keyEvent);
            }
        }
    }

    private static class MouseAdapter extends SwingMouseAdapter {
        public void mouseClicked(MouseEvent mouseEvent) {
            MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].mouseClicked(mouseEvent);
            }
        }

        public void mousePressed(MouseEvent mouseEvent) {
            MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].mousePressed(mouseEvent);
            }
        }

        public void mouseReleased(MouseEvent mouseEvent) {
            MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].mouseReleased(mouseEvent);
            }
        }

        public void mouseEntered(MouseEvent mouseEvent) {
            MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].mouseEntered(mouseEvent);
            }
        }

        public void mouseExited(MouseEvent mouseEvent) {
            MouseListener[] listeners = eventListeners.getListeners(MouseListener.class);

            for (int i = 0; i < listeners.length; i++) {
                listeners[i].mouseExited(mouseEvent);
            }
        }
    }

    public static void addSwingKeyListener(KeyListener listener) {
        if (listener != null) {
            eventListeners.add(KeyListener.class, listener);
        }
    }

    public static void removeKeyListener(KeyListener listener) {
        if (listener != null) {
            eventListeners.remove(KeyListener.class, listener);
        }
    }

    public static void addMouseListener(MouseListener listener) {
        if (listener != null) {
            eventListeners.add(MouseListener.class, listener);
        }
    }

    public static void removeMouseListener(MouseListener listener) {
        if (listener != null) {
            eventListeners.remove(MouseListener.class, listener);
        }
    }
}

所有这一切都是使用父全局屏幕注册库中包含的几个适配器类,并调度到适配器中的swing侦听器。还有其他方法可以更强大,比如扩展GlobalScreen.NativeHookThread匿名内部类,但这会占用更多代码。

处理Java FX与swing非常相似,您可以在我之前的帖子中找到一个示例调度程序:https://stackoverflow.com/a/45423466/773849

.consume()而言,是的,这是不受支持的,但在某些平台上仍然可行。如果您使用的是Windows或OS X,则可以使用wiki中概述的一些聪明代码来停止事件传播。这将要求您覆盖前面提到的NativeHookThread内部类,并且您必须快速处理事件,因为在这种情况下NativeHookThread实际上不是一个线程并且将阻止操作系统。这种方法在Linux上不可用,因为linux目前无法执行此功能。