提问人:Bernd 提问时间:9/21/2023 更新时间:9/23/2023 访问量:62
如何让 Java 输出到我关注的地方?(例如:打开文本文档)
How do I get Java to output to where my focus is? (example: open text document)
问:
我使用 jnativehook.keyboard.NativeKeyListener 来收听在打开的文本文档中键入的键。这里没问题。 经过短暂的排列(想想凯撒密码),我想将输出发送回那个打开的文本文档。 我该怎么做?这似乎出奇地复杂......
答:
-1赞
JayC667
9/21/2023
#1
实际上,与你帖子的评论者所说的相反,有一种 Java 方法可以使用机器人发送关键事件。
Robot bot = new Robot();
(java.awt.Robot
)如果您有 as in,则可以使用以下方式将该密钥发回
NativeKeyEvent pKeyEvent
org.jnativehook.keyboard.NativeKeyEvent
bot.keyPress(pKeyEvent.getRawCode()); actionSleep(); // sleep for as long as you need, normal is ~20-60 ms bot.keyRelease(pKeyEvent.getRawCode());
如果您有 Java Swing/AWT KeyEvent(如 ),则可以像这样使用它:
KeyEvent pKeyEvent
java.awt.event.KeyEvent
bot.keyPress(pKeyEvent.getKeyCode()); actionSleep(); // sleep bot.keyRelease(pKeyEvent.getKeyCode());
如果你想通过代码发送密钥,你可以使用它(用于按下和释放密钥“a”):
bot.keyPress(KeyEvent.VK_A); actionSleep(); // sleep bot.keyRelease(KeyEvent.VK_A);
机器人还具有一些附加功能,例如屏幕捕获/屏幕截图、屏幕像素读取、控制鼠标。
示例应用:
package stackoverflow.keylistenercaesar;
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
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;
import jc.lib.lang.thread.JcUThread;
/**
* Written with jnativehook_2.2.0<br>
* <br>
* <b>Important!</b> jnativehook_2.1.0 does NOT work correctly under windows, because it does not generate addNativeKeyListener->nativeKeyTyped events!
*
* @author jc
* @since 2023-09-23
*
*/
public class KeyListenerCaesarChiffre {
static public final int KEY_PRESS_DURATION_MS = 1;
static private StringBuilder sRecordedChars = new StringBuilder();
static private ArrayList<Integer> sRecordedRawKeyCodes = new ArrayList<>();
static private Robot sRobot;
static private volatile boolean sRecordKeys;
static public void main(final String[] args) throws NativeHookException {
// disable the stupid logger
final Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
logger.setLevel(Level.WARNING);
logger.setUseParentHandlers(false);
// set up key hook listening
GlobalScreen.registerNativeHook();
GlobalScreen.addNativeKeyListener(new NativeKeyListener() {
@Override public void nativeKeyPressed(final NativeKeyEvent pKeyEvent) {
// System.out.println("nativeKeyPressed(c" + pKeyEvent.getKeyCode() + " r" + pKeyEvent.getRawCode() + " c" + pKeyEvent.getKeyChar() + ")");
}
@Override public void nativeKeyReleased(final NativeKeyEvent pKeyEvent) {
// System.out.println("nativeKeyReleased(c" + pKeyEvent.getKeyCode() + " r" + pKeyEvent.getRawCode() + " c" + pKeyEvent.getKeyChar() + ")");
try {
controlKeyReleased(pKeyEvent);
} catch (final Exception e) {
e.printStackTrace();
}
}
@Override public void nativeKeyTyped(final NativeKeyEvent pKeyEvent) {
// System.out.println("nativeKeyTyped(c" + pKeyEvent.getKeyCode() + " r" + pKeyEvent.getRawCode() + " c" + pKeyEvent.getKeyChar() + ")");
try {
inputKeyTyped(pKeyEvent);
} catch (final Exception e) {
e.printStackTrace();
}
}
});
// print info
System.out.println("Key Bindings");
System.out.println("\tKey\tUse");
System.out.println("\t------\t------");
System.out.println("\tF2\tStart Listening");
System.out.println("\tF3\tEnd Listening");
System.out.println("\tF4\tExit app");
System.out.println("\tEnter\tTrigger caesar conversion + output");
}
/**
* This method will be called each time any key is released. So we have to filter carefully
*/
static protected void controlKeyReleased(final NativeKeyEvent pKeyEvent) throws NativeHookException {
switch (pKeyEvent.getKeyCode()) {
case NativeKeyEvent.VC_F2: {
System.out.println("Start listening...");
sRecordKeys = true;
break;
}
case NativeKeyEvent.VC_F3: {
System.out.println("Stop listening...");
sRecordKeys = false;
break;
}
case NativeKeyEvent.VC_F4: {
System.out.println("Shutting down...");
GlobalScreen.unregisterNativeHook();
break;
}
case NativeKeyEvent.VC_ENTER: {
if (sRecordKeys) runCasesarAndOutput();
break;
}
default: // ignore the rest
}
}
/**
* This method will only get triggered for valid input chars. So no control codes like F1, F2 etc will arrive here
*/
static protected void inputKeyTyped(final NativeKeyEvent pKeyEvent) {
if (!sRecordKeys) return;
// System.out.println("KeyListenerCaesarChiffre.inputKeyTypedx(" + pKeyEvent.getKeyCode() + " r" + pKeyEvent.getRawCode() + " c" + pKeyEvent.getKeyChar() + ")");
switch (pKeyEvent.getKeyChar()) {
default: {
// System.out.println("Key:\t" + pKeyEvent.getKeyCode() + " / " + pKeyEvent.getRawCode() + " => " + pKeyEvent.getKeyChar());
sRecordedChars.append(pKeyEvent.getKeyChar());
sRecordedRawKeyCodes.add(Integer.valueOf(pKeyEvent.getRawCode()));
System.out.println("\tlen:" + sRecordedChars.length() + "\ttext:" + sRecordedChars.toString());
}
}
}
/**
* This will need to run in its own thread.
* If we do not, we would run in the key event listener thread, and all keys simulated here would only be processed AFTER this method is done,
* so it would record its own inputs.
*/
static private void runCasesarAndOutput() {
new Thread(() -> {
try {
runCasesarAndOutput_();
} catch (final AWTException e) {
e.printStackTrace();
}
}).start();
}
static private void runCasesarAndOutput_() throws AWTException {
// wait until enter key event is processed
sleep(50);
try {
// suspend key listening temporaily or we will feed back into our own input
sRecordKeys = false;
// send output. Do NOT process the enter key, thus size-1
for (int i = 0; i < sRecordedRawKeyCodes.size() - 1; i++) {
final int c = sRecordedRawKeyCodes.get(i).intValue();
typeKey(c + 1); // this is a really bad implementation of caesar
// especially because this will fall out of the keymapping boundaries a lot!
// some normal chars might get converted into control chars, like the arrow keys, for example
// also, letters do not wrap back up (z does not wrap back up to a in case of +1)
// also, you need to take additional care of special control keys like backspace the handle the ensuing "text" properly!
}
// send enter to finish line
typeKey(KeyEvent.VK_ENTER);
} finally {
sRecordKeys = true;
}
// clear intercepted key chars
sRecordedChars.setLength(0);
sRecordedRawKeyCodes.clear();
}
static public void typeKey(final int pKeyCode) throws AWTException {
if (sRobot == null) sRobot = new Robot();
try {
sRobot.keyPress(pKeyCode);
sleep(KEY_PRESS_DURATION_MS);
sRobot.keyRelease(pKeyCode);
// sleep(KEY_PRESS_DURATION_MS);
} catch (final java.lang.IllegalArgumentException e) {
System.err.println("Key code [" + pKeyCode + "] is invalid!");
e.printStackTrace();
}
}
static public void beep(final int pCount) {
final Toolkit dtk = Toolkit.getDefaultToolkit();
for (int i = 0; i < pCount; i++) {
dtk.beep();
JcUThread.sleep(300);
}
}
static public void sleep(final int pMS) {
try {
Thread.sleep(pMS);
} catch (final InterruptedException e) { /* */ }
}
}
这种实现有很多缺点和问题本身:
- 需要 jnativehook_2.2.0,因为早期版本在 Windows 下无法可靠地工作
- 在处理控制键(如退格键、箭头键等)时需要特别小心。
- 凯撒削片机将是一个痛苦的屁股,无法正确实施
- 评论中的更多详细信息
用法:
- 运行应用
- 进入您的文本编辑器
- 按 F2 开始录制按键
- 当您按 Enter 键时,按键将被凯撒转换,然后作为击键发送到文档
- 按 F3 停止录制
- 或按 F4 关闭应用
评论
1赞
Hovercraft Full Of Eels
9/21/2023
我已经提到过机器人。不过,问题在于将注意力集中在正确的独立流程上,这是您的答案无法解决的问题,除非我遗漏了什么。
0赞
JayC667
9/21/2023
是的,但你暗示了重点话题,即使没有提到它,似乎也不是必需的。这篇文章提到了在后台运行的 Java 应用程序,在前台监听文本编辑的文本/击键。然后通过凯撒密码进行处理,这也可以在后台完成。所以对我来说,听起来没有必要“更改活动应用程序”,因为该文本编辑器仍然具有焦点。否则,要使用 Alt+Tab 和 stackoverflow.com/questions/34992027/...
0赞
Bernd
9/22/2023
我以为我会简单地反转 NativeKeyListener,因为它已经知道哪个窗口处于焦点:System.out.FocusOwner(...) 或类似窗口。
0赞
Bernd
9/22/2023
澄清:是的,我不打算改变打开文本文档的重点,但它可能会发生(随机电子邮件等)。谢谢,我将研究机器人,然后是 JNI/JNA。为了方便起见,我使用 Windows,但我打算在 MacOS 上使用最终应用程序。
0赞
Bernd
9/23/2023
@JayC667,我尝试了机器人,但根本没有输出。你能写一些以“public void nativeKeyTyped(NativeKeyEvent pKeyEvent)”开头的演示代码吗?
评论