跳到主要内容

Java 监听Mac键盘输入实现快捷键功能

背景需求

当你想使用Java 给Mac上注册快捷键时,你百度到可以通过 jnativehook进行实现。然后你发现,下载下来的例子进行运行不了,这篇文章说的就是怎么解决这个问题,以及如果通过这个 jnativehook实现Mac上的系统级快捷键。

jnativehook

JNativeHook 为 Java 程序提供全局的键盘和鼠标事件侦听功能。你可以来处理程序外的键盘输入和鼠标动作。当然 JNativeHook 使用了JNI 技术调用了系统的方法来实现该功能。

github地址: https://github.com/kwhat/jnativehook

引用正确的dependency

如果你是在maven仓库中进行搜索的话,你会发现它有好几个来源 image.png

此时,不应该点开第一个,因为当你使用第一个的时候就会导致之后的Simple Code都执行不了。

正确的应该是选择最新更新2021年的那个,也就是它

image.png

简单例子

jnativehook 的github readme.md页面上其实提到了一个很实用的例子,可以满足绝大多数的需求了。

image.png

OK,此时你新建了一个Maven项目,然后引入了刚刚说到的最新的dependency,我想是没错的。

然后当你复制这个Demo进行启动main方法的时候,第一次执行mac会弹出提示说,给予Applicaiton权限。

类似以下画面,授予权限就行

image.png

程序的成功执行页面是这样的。每当你在触控板上进行移动,键盘输出内容,都会进行输出,这样就体现了这个工具的强大性,可以实时的监听你的操作。

image.png

注册全局快捷键

ok,完成以上内容之后,现在我们该说说代码了。在使用的过程中 jnativehook似乎没有具体对于快捷键的实现,也就是这一部分需要我们进行完成。

当然在完成上面那个小demo之后我猜你也应该猜到怎么做了,既然我们能监听到mac的所有输入了,那假如连续输入对应的键然后触发相应的功能就是实现了快捷键的功能。

ok,来一个小功能先,就是知道怎么只监听所有的键盘输入

Simple: https://github.com/kwhat/jnativehook/blob/2.2/doc/Keyboard.md

import com.github.kwhat.jnativehook.GlobalScreen;
import com.github.kwhat.jnativehook.NativeHookException;
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent;
import com.github.kwhat.jnativehook.keyboard.NativeKeyListener;

public class GlobalKeyListenerExample implements NativeKeyListener {
public void nativeKeyPressed(NativeKeyEvent e) {
System.out.println("Key Pressed: " + NativeKeyEvent.getKeyText(e.getKeyCode()));

if (e.getKeyCode() == NativeKeyEvent.VC_ESCAPE) {
try {
GlobalScreen.unregisterNativeHook();
} catch (NativeHookException nativeHookException) {
nativeHookException.printStackTrace();
}
}
}

public void nativeKeyReleased(NativeKeyEvent e) {
System.out.println("Key Released: " + NativeKeyEvent.getKeyText(e.getKeyCode()));
}

public void nativeKeyTyped(NativeKeyEvent e) {
System.out.println("Key Typed: " + e.getKeyText(e.getKeyCode()));
}

public static void main(String[] args) {
try {
GlobalScreen.registerNativeHook();
}
catch (NativeHookException ex) {
System.err.println("There was a problem registering the native hook.");
System.err.println(ex.getMessage());

System.exit(1);
}

GlobalScreen.addNativeKeyListener(new GlobalKeyListenerExample());
}
}

上面实现的功能就是,键盘的任何输入都会进行输出打印。

那么下一步,就是对当中的方法 nativeKeyPressed进行一下改造,这里做一个简单的实现,假设现在我的想要实现的快捷键是: Ctrl + Command + P 进行触发功能

那么对当中的方法 nativeKeyPressed 进行改造

    protected final static LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue();

public void nativeKeyPressed(NativeKeyEvent e) {
//当有键盘输入的时候,放入队列
try {
queue.put(e.getKeyCode());
} catch (InterruptedException ex) {
ex.printStackTrace();
}

// Ctrl + command + p
int[] hotKeyArray = {NativeKeyEvent.VC_CONTROL, NativeKeyEvent.VC_META, NativeKeyEvent.VC_P};
//如果队列中的数据大于等于3,那就进行判断是不是包含连续且等于我们指定的键的顺序
//如果存在就进行执行处罚
if (queue.size() >= 3 && judgeCombinationKey(hotKeyArray)){
try {
do something.........
} catch (InterruptedException ex) {
ex.printStackTrace();
}
queue.clear();
}
if (queue.size() == 4){
queue.poll();
}
}

protected Boolean judgeCombinationKey(int[] hotKeyArray){
Object[] queueKey = queue.toArray();

Predicate<int[]> keyArrayPredicateOne = hotKeies -> (int)queueKey[0] == hotKeies[0]
&& (int)queueKey[1] == hotKeies[1]
&& (int)queueKey[2] == hotKeies[2];

Predicate<int[]> keyArrayPredicateTwo = hotKeies -> (int)queueKey[1] == hotKeies[0]
&& (int)queueKey[2] == hotKeies[1]
&& (int)queueKey[3] == hotKeies[2];

return queue.size() == 3 ? keyArrayPredicateOne.test(hotKeyArray) :
keyArrayPredicateOne.or(keyArrayPredicateTwo).test(hotKeyArray);

}

好了,当完成以上的内容其实就已经完成了!