鉴于有朋友反馈说不会封装和不会用,话不多说,Demo送上: https://download.csdn.net/download/qq_20698983/11474704
**这是我第一次写博客,写的不好大家见谅!**
其实Java实现一个软键盘是非常简单的一件事情,但是实现一个软键盘只能在当前这个打开的窗口输入中英文的话只会让你觉得没有一点成就感
所以,我搜集了一些相关的资料,然后封装了一点代码,可以实现向任意窗口发送你的软键盘生成的信息(例如:QQ、Notepad、记事本等等)
用到的一些jar:
1:jna-4.2.1.jar
2:jna-platform-4.0.0.jar
jna源码地址:https://github.com/java-native-access/jna#readme
jna下载地址:https://maven.java.net/content/repositories/releases/net/java/dev/jna/jna/4.2.1/
关键技术:
SendInput
转载请注明出处(嗯,我看别人的资料也很喜欢写这句话)
特别注意:此代码不提供软键盘的代码,写个软件盘按我最后列出的“使用说明”调用这个就行了
建议将我这个代码封装成一个jar,然后给你的代码去调用!
Logger.java
package cn.xt.log;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Logger {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static boolean openLog = true; // 默认打开日志
public static void debug(String logs) {
if (openLog) {
System.out.println(sdf.format(new Date()) + " [INFO] - " + logs);
}
}
public static boolean isOpenLog() {
return openLog;
}
public static void setOpenLog(boolean openLog) {
Logger.openLog = openLog;
}
// public static void main(String[] args) {
// Logger.debug("数据异常");
// }
}
ApiConst.java
package cn.xt.api;
/**
* WinAPI的一些常量
*/
public class ApiConst {
public static final int WM_MOUSEACTIVATE = 0x21; // 获得扩展窗口风格
public static final int GWL_EXSTYLE = -20; // 获得扩展窗口风格
public static final int WS_EX_NOACTIVATE = 0x8000000; // 不激活窗口
public static final int MA_NOACTIVATE = 3; // 鼠标进来不获得焦点
}
MyUser32.java
package cn.xt.api;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.win32.StdCallLibrary.StdCallCallback;
import com.sun.jna.win32.W32APIOptions;
/**
* 自定义的一个WinApi接口
*/
public interface MyUser32 extends Library {
static MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("user32", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS);
interface WindowProc extends StdCallCallback {
LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam);
}
abstract WindowProc SetWindowLongPtr(HWND hWnd, int nIndex, WindowProc dwNewLong);
abstract WindowProc GetWindowLongPtr(HWND hWnd, int nIndex);
abstract HWND GetFocus();
}
最后,重点在这里
WinUtil.java
package cn.xt.util;
import java.awt.Component;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LPARAM;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.INPUT;
import cn.xt.api.ApiConst;
import cn.xt.api.MyUser32;
import cn.xt.api.MyUser32.WindowProc;
import cn.xt.log.Logger;
public class WinUtil {
private static User32 sysLib = User32.INSTANCE;
private static MyUser32 userLib = MyUser32.INSTANCE;
private static boolean isOk = true; // 是否正常加载主窗口句柄
private static HWND hwnd = null; // 主窗口句柄
/**
* 初始化工具类
* @param c 传入一个窗口对象(Component或它的子类)
* @param title 传入窗口的标题(此参数和 Component 可任意一个为空)
*/
public static void init(Component c, String title) {
System.setProperty("jna.encoding", "utf-8");
Pointer pointer = null;
if(c != null){
pointer = Native.getComponentPointer(c);
}
if(pointer == null){
Logger.debug("无法找到窗口对象,正在按标题查找...");
hwnd = sysLib.FindWindow(null, title);
} else {
hwnd = new HWND(pointer);
}
if(hwnd == null){
Logger.debug("无法找到窗口句柄,请检查窗口标题或窗口是否存在!");
isOk = false;
return;
}
Logger.debug("找到主窗口的句柄为:" + hwnd.toString());
/**
* 进行窗口监听
* 这里监听Window通知 JavaGUI绘制的一些命令
*/
final WindowProc thisFrameEventHander = userLib.GetWindowLongPtr(hwnd, User32.GWL_WNDPROC);
// 为自定义事件创建回调函数,并将其他消息继续转发原来的事件处理函数
WindowProc proc = new WindowProc() {
// 处理一个应用级别的消息(自定义消息)
@Override
public LRESULT callback(HWND hWnd, int uMsg, WPARAM wParam, LPARAM lParam) {
// 可以在这里设置一些窗口消息监听之类的事情
if (uMsg == ApiConst.WM_MOUSEACTIVATE) {
uMsg = ApiConst.MA_NOACTIVATE;
}
// Logger.debug(String.valueOf(uMsg) + " " + wParam + " " + lParam);
// 为了不影响该Java GUI 窗口,还需要把其他消息继续转发给原有的事件处理函数。
return thisFrameEventHander.callback(hWnd, uMsg, wParam, lParam);
}
};
boolean registerWindow = (userLib.SetWindowLongPtr(hwnd, User32.GWL_WNDPROC, proc) != null ? true : false);
int registerNOActivate = sysLib.SetWindowLong(hwnd, ApiConst.GWL_EXSTYLE,
sysLib.GetWindowLong(hwnd, ApiConst.GWL_EXSTYLE) | ApiConst.WS_EX_NOACTIVATE);
Logger.debug("注册窗口监听:" + registerWindow);
Logger.debug("注册无状态栏样式:" + (registerNOActivate > 0 ? true : false));
}
/**
* 按键操作
* @param keyCode 传入的键标码
*/
public synchronized static void keyPress(int keyCode) {
if(!isOk) {
JOptionPane.showMessageDialog(null, "系统初始化失败,请重新初始化");
return;
}
Logger.debug("当前传入的键标码:" + keyCode);
HWND currHwnd = userLib.GetFocus();
attachThreadInput(currHwnd, true);
Logger.debug("当前被选择的窗口标题:" + getCurrTitle());
// User32.INSTANCE.SetForegroundWindow(currHwnd);
User32.INSTANCE.SetFocus(currHwnd);
keyPressed(keyCode);
keyRealesed(keyCode);
attachThreadInput(currHwnd, false);
}
/**
* 亲和或释放线程
* @param hwnd
* @param isAttach
*/
private static void attachThreadInput(HWND hwnd, boolean isAttach) {
DWORD prepDWORD = new DWORD(User32.INSTANCE.GetWindowThreadProcessId(hwnd, null));
DWORD currDWORD = new DWORD(Kernel32.INSTANCE.GetCurrentThreadId());
Logger.debug((isAttach ? "亲和" : "释放") + "连接对象:" + prepDWORD.toString() + " --> " + currDWORD.toString());
User32.INSTANCE.AttachThreadInput(prepDWORD, currDWORD, isAttach);
}
/**
* 获取当前选择的窗口标题
*/
private static String getCurrTitle(){
HWND hwnd = sysLib.GetForegroundWindow();
int titleSize = sysLib.GetWindowTextLength(hwnd) + 1;
char[] windowTitle = new char[titleSize];
sysLib.GetWindowText(hwnd, windowTitle, titleSize);
return String.valueOf(windowTitle);
}
/**
* 键按下事件
* @param keyCode
*/
private synchronized static void keyPressed(int keyCode) {
INPUT ip = new INPUT();
ip.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
ip.input.setType("ki");
ip.input.ki.wScan = new WinDef.WORD(0);
ip.input.ki.time = new WinDef.DWORD(Kernel32.INSTANCE.GetTickCount());
ip.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
ip.input.ki.wVk = new WinDef.WORD(keyCode);
ip.input.ki.dwFlags = new WinDef.DWORD(0);
User32.INSTANCE.SendInput(new WinDef.DWORD(1), (INPUT[]) ip.toArray(1), ip.size());
sleep(10);
}
/**
* 键抬起事件
* @param keyCode
*/
private synchronized static void keyRealesed(int keyCode) {
INPUT ip = new INPUT();
ip.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
ip.input.setType("ki");
ip.input.ki.wScan = new WinDef.WORD(0);
ip.input.ki.time = new WinDef.DWORD(Kernel32.INSTANCE.GetTickCount());
ip.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
ip.input.ki.wVk = new WinDef.WORD(keyCode);
ip.input.ki.dwFlags = new WinDef.DWORD(WinUser.KEYBDINPUT.KEYEVENTF_KEYUP | 0x4);
User32.INSTANCE.SendInput(new WinDef.DWORD(1), (INPUT[]) ip.toArray(1), ip.size());
sleep(10);
}
/**
* 线程休眠
* @param millis
*/
private static void sleep(long millis){
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Logger.debug(e.getMessage());
}
}
public static void main(String[] args) throws Exception {
Logger.setOpenLog(false); // 是否打开日志
JFrame frame = new JFrame("test windows");
frame.add(new JTextField());
frame.setSize(300, 300);
frame.setLocation(0, 0);
frame.setVisible(true);
frame.setDefaultCloseOperation(3);
init(null, "test windows"); // 初始化窗口
keyPress(KeyEvent.VK_A); //
keyPress(KeyEvent.VK_B);
}
}
使用方式:
1:将此目录的三个 .jar结尾的项目导入你的项目中
WinUtil 提供几个方法供调用
1:public static void init(Component c, String title);
功能:初始化系统相关属性
参数:
一:c 为你的容器对象
二:title 为你的窗口标题
注意:两个参数可任一一个为空,也可以全写,系统会优先检索 c,如果c不存在再检索 title
调用方式示例:
WinUtil.init(null, "输入演示"); // 这几个字为你的系统标题
WinUtil.init(MyJFrame, null); // MyJFrame 为一个窗口对象
2:public synchronized static void keyPress(int keyCode);
功能:传入要发送的 键代码
参数:keyCode 如:KeyEvent.VK_A
调用方式示例:
WinUtil.keyPress(KeyEvent.VK_A); // 传入一个字母 a的 keyCode
日志类
Logger.setOpenLog(boolean isOpen);
功能:是否打开日志
调用方式示例:
Logger.setOpenLog(true); // 打开日志
Logger.setOpenLog(false); // 关闭日志
功能调用注意事项
Logger.setOpenLog(); 可在任意时间调用
WinUtil.init(); 在你的窗口显示以后才能调用
WinUtil.keyPress(); 任意时间调用