代码import cn.hutool.core.collection.CollUtil;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 监听键盘和鼠标按键, 这个是强制顺序和不支持重复的
* 如果要支持重复就得使用List保存快捷键, 并给每个编键去配置标志位, 或者快捷键数组使用对象数组
* 如果要不要求顺序, 就按下时候直接标志位变成true即可
*
* @author codor
* @date 2023/12/08 16:23
* @see org.jnativehook.example.NativeHookDemo
*/
public class NativeApplication extends JFrame implements NativeKeyListener {
// ----------------------------------------------------------------------------------------------------------------- 逻辑使用
/**
* 快捷键组合
* 名字也可以不写上去, 使用NativeKeyEvent.getKeyText(event.getKeyCode())获取
*/
private Map<Integer, String> hotKeys;
/**
* 键编码的列表, 因为要保证顺序
*/
private List<Integer> codes;
/**
* 快捷键是否按下标标志位
*/
private Map<Integer, Boolean> flags;
/**
* 窗口是否展示出来了
*/
private boolean isWindowShow = false;
// ---------------------------------------------------------------------------------------------------------------- 界面使用
/**
* 倒计时
*/
private Timer countdownTimer;
/**
* 倒计时事件
*/
private final static Integer COUNT_DOWN_SECONDS = 10;
/**
* 初始化标志位
*
* @see org.jnativehook.keyboard.NativeKeyEvent
*/
public void initHotKeys() {
this.hotKeys = new LinkedHashMap<>();
this.hotKeys.put(29, "Ctrl");
this.hotKeys.put(46, "C");
this.hotKeys.put(45, "X");
this.hotKeys.put(47, "V");
// 初始化键编码
this.codes = new ArrayList<>();
this.hotKeys.forEach((k, v) -> this.codes.add(k));
// 初始化标指位
this.initFlags();
}
/**
* 初始化标志位
*/
public void initFlags() {
if (this.hotKeys == null) {
this.initHotKeys();
}
this.flags = new HashMap<>();
for (Integer code : this.codes) {
this.flags.put(code, false);
}
}
/**
* 按下键时候, 强制要求顺序
*
* @param code 键编码
* @return 是否全部按下了, true是, 反之false
*/
public boolean pressedKey(int code) {
// 如果未在内, 重置标志位
if (!this.codes.contains(code)) {
this.initFlags();
return false;
}
for (Integer k : this.codes) {
// 循环到 第一个非按下状态的就跳出循环或退出方法
if (!this.flags.get(k)) {
// 并且等于按下的键, 设置按下成功
if (k.equals(code)) {
this.flags.put(code, true);
break;
} else { // 不是不等于按下的, 说明按了后面的,
this.initFlags();
return false;
}
}
}
// 只有break时候到这里, 判断是否循环完, 也即code是否最后一个
return code == this.codes.get(this.codes.size() - 1);
}
/**
* 松开键
*/
public void releaseKey(int code) {
this.flags.put(code, false);
}
public String getKeyTexts() {
if (CollUtil.isEmpty(this.codes)) {
return "";
}
StringBuilder resB = new StringBuilder(" ");
for (Integer code : this.codes) {
resB.append(NativeKeyEvent.getKeyText(code)).append(" ");
}
return resB.toString();
}
public void showWindow(String hotKeyTexts) {
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
setUndecorated(true); // 窗口无装饰, 但是标题也会干掉
setTitle("倒计时窗口");
setLayout(new BorderLayout());
JLabel countdownLabel = new JLabel("倒计时: " + COUNT_DOWN_SECONDS + " 秒", JLabel.CENTER);
JButton cancelButton = new JButton("取消");
cancelButton.addActionListener(e -> {
// 点击取消按钮时的处理
if (countdownTimer != null) {
// 重置标志位
this.initFlags();
// 关闭窗口
this.closeWindow();
}
});
add(countdownLabel, BorderLayout.CENTER);
add(cancelButton, BorderLayout.SOUTH);
setSize(300, 150);
setLocationRelativeTo(null); // 将窗口置于屏幕中央
setVisible(true);
AtomicInteger second = new AtomicInteger(COUNT_DOWN_SECONDS);
countdownTimer = new Timer(1000, e -> {
second.getAndDecrement();
if (second.get() >= 0) {
countdownLabel.setText("倒计时: " + second.get() + " 秒");
} else {
// 关闭窗口
this.closeWindow();
// 触发成功后发送指令
// 指令发送成功后重置标志位
this.initFlags();
}
});
countdownTimer.start();
}
public void closeWindow() {
countdownTimer.stop();
dispose(); // 关闭窗口
setVisible(false); // 设置窗口不可见
getContentPane().removeAll(); // 移除所有组件
revalidate(); // 重新验证布局
repaint(); // 重绘窗口
// 关闭窗口时候重置
isWindowShow = false;
}
// ---------------------------------------------------------------------------------------------------------------- Override Method
@Override
public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {
}
@Override
public void nativeKeyPressed(NativeKeyEvent event) {
boolean isHotKey = this.pressedKey(event.getKeyCode());
// 成功并且窗口未展示时候
if (isHotKey && !isWindowShow) {
System.err.println("触发快捷组合键成功...");
// 窗口标志位设置成功
isWindowShow = true;
this.showWindow(this.getKeyTexts());
}
}
@Override
public void nativeKeyReleased(NativeKeyEvent event) {
this.releaseKey(event.getKeyCode());
}
public static void main(String[] args) {
try {
// 设置日志级别,避免 JNativeHook 的日志输出
Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
logger.setLevel(Level.OFF);
// 注册全局键盘事件
GlobalScreen.registerNativeHook();
} catch (NativeHookException ex) {
ex.printStackTrace();
System.err.println("Failed to initialize native hook. Exiting.");
System.exit(1);
}
// 添加 NativeKeyListener
NativeApplication application = new NativeApplication();
application.initFlags();
GlobalScreen.addNativeKeyListener(application);
}
}