是否可以使用javafx方法.setOnKeyPressed()来实现jnativehook?
例如,这是它在应用程序聚焦时监听按键的方式:
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>
我确信我需要重载该方法以考虑这些参数,但是我不确定如何去做以及如何处理第二个错误。
方法 .consume() 是 unsupported对于 jnativehook。
或者是否有另一种方法可以在 javafx 中上下移动 ListView 而不使用这些方法并且与 jnativehook 兼容?
最佳答案
首先,我们来谈谈事件传递和 UI 线程,因为了解线程和线程之间共享的内存在幕后进行的上下文切换非常重要。
与几乎所有其他设计的应用程序一样,Java 对所有 UI 事件使用单个线程。其原因是well documented遍布互联网。因此,如果您使用 AWT/Swing 或 Java FX,您的事件将分别在 AWT/Swing 或 Java FX 平台线程上分派(dispatch),并且其他线程不应交互或期待这些事件。
JNativeHook 使用自己的调度线程,完全独立于 Java 的 UI。这样做主要是出于兼容性原因,因为我不想对实现用户强制使用特定的 UI API。因此,如果您想使用 AWT/Swing、Java FX 或其他工具,则必须在负责 UI 的线程上下文上分派(dispatch) native 事件。库中有一个内置的 Spring 调度程序,以及我发布的一个 Java FX 调度程序 here通过向 GlobalScreen 注册调度程序,可以通过默认调度方法进行非常简单的上下文切换。
您可能会问自己,“如果我想在分派(dispatch)事件之前修改事件怎么办?”这也应该是可能的,但是比注册调度类需要更多的努力。您应该能够扩展 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);
}
}
}
这一切所做的就是向父全局屏幕注册库中包含的几个适配器类,并分派(dispatch)到适配器中的 swing 监听器。还有其他更强大的方法可以做到这一点,例如扩展 GlobalScreen.NativeHookThread匿名内部类,但这将需要您花费更多的代码。
处理 Java FX 与 swing 非常相似,您可以在我之前的文章中找到一个调度程序示例:https://stackoverflow.com/a/45423466/773849
就 .consume()
而言,是的,这不受支持,但在某些平台上仍然可能。如果您使用的是 Windows 或 OS X,您可以使用一些巧妙的代码来停止事件传播,如 wiki 中所述。 。这将要求您重写前面提到的 NativeHookThread 内部类,并且您必须快速处理事件,因为在这种情况下 NativeHookThread 并不是真正的线程,并且会阻塞操作系统。这个方法在Linux上不可用,因为Linux目前还没有办法执行这个功能。
关于java - 为 javafx 方法实现全局按键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48951220/