尝试使用 JAVA 读取 Scroll Lock 状态

Posted

技术标签:

【中文标题】尝试使用 JAVA 读取 Scroll Lock 状态【英文标题】:Trying to read Scroll Lock status using JAVA 【发布时间】:2020-08-05 03:08:39 【问题描述】:

我有下面的代码行,我正在尝试读取滚动锁定的状态。

当我的程序启动时,我得到了滚动锁定的状态。 但是我愿意实时获得状态。 请在下面指导

package assignment;

import java.awt.Toolkit;
import java.awt.event.KeyEvent;

import org.omg.PortableServer.THREAD_POLICY_ID;

public class ScrollLockOnOff 

    public static void main(String[] args) throws InterruptedException 
    
        while(true)
        
            Thread.sleep(1000);
            Toolkit toolkit=Toolkit.getDefaultToolkit();
            System.out.println(toolkit.getLockingKeyState(KeyEvent.VK_SCROLL_LOCK));
        
    



【问题讨论】:

this 的副本。 是的,这可能是重复的.. 我相信该帖子中的实际问题没有解决方案.. “实时获取状态”到底是什么意思?在您上面的示例中,您似乎希望每秒获取一次状态。您看到的行为是什么? 按照编写的代码..我应该每秒得到滚动锁定的状态......但是,我得到了程序启动时的状态...... 导入org.omg.PortableServer.THREAD_POLICY_ID有什么意义? 【参考方案1】:

这是一种有趣的行为,可以正确报告初始状态,但要依赖后续事件处理(具有聚焦的***窗口或托盘图标)进行更新。

如果我们有办法将 AWT 重置为其初始状态,它应该可以解决问题。如果我们没有找到这种可能性,直接的解决方法是运行一个新的 JVM。由于具有相同属性的新 JVM 将使用缓存甚至共享内存中的资源,因此开销比听起来要小得多。每秒执行一次动作没问题:

public class ScrollLockOnOff 
    public static void main(String[] args)
                       throws InterruptedException, AWTException, IOException 

        if(args.length == 1 && args[0].equals("VK_SCROLL_LOCK")) 
            System.exit(Toolkit.getDefaultToolkit()
                .getLockingKeyState(KeyEvent.VK_SCROLL_LOCK)? KeyEvent.VK_SCROLL_LOCK: 0);
            return;
        

        ProcessBuilder b = new ProcessBuilder(
            Paths.get(System.getProperty("java.home"), "bin", "java").toString(),
            "-classpath", System.getProperty("java.class.path"),
            ScrollLockOnOff.class.getName(), "VK_SCROLL_LOCK"
        ).inheritIO();

        while(true) 
            Thread.sleep(1000);
            int state = b.start().waitFor();
            if(state != 0 && state != KeyEvent.VK_SCROLL_LOCK) 
                System.err.println("failed");
                break;
            
            System.out.println(state == KeyEvent.VK_SCROLL_LOCK);
        
    

【讨论】:

这是我想我们可以拥有的最接近的解决方案......虽然再次启动 jvm 似乎是一个开销...... 或等到此行为得到修复。考虑到有可以追溯到 JDK 1.4 时代的 bug 报告,我们只能推测这是否会发生以及何时会发生。答案中已经提到了开销。在今天的 JVM 上,它比过去少了,这要归功于共享库、类数据归档、内存映射等。 我同意.. 如果我们在明天之前没有其他答案,我会接受这个答案......【参考方案2】:

如果您想监听滚动锁定的按键,请执行 key listener 寻找 KeyEvent.VK_SCROLL_LOCK

通常(以及getLockingKeyState() 方法的原因)您会监听其他事件(例如向上/向下箭头键按下),然后会查询滚动锁定键的状态以决定如何解释该事件.但是应该可以像任何其他键一样监听滚动锁定按下。

我的笔记本电脑键盘上没有滚动锁定键,但是当我运行链接教程中的 KeyEventDemo.java 示例时,我看到它捕获了 KeyEvent.VK_CAPS_LOCK 事件;打开 Caps Lock 时会触发“KEY PRESSED”事件,关闭时会触发“KEY RELEASED”事件。 Scroll Lock 的行为应该类似。

我在KeyEventDemo.javadisplayInfo() 方法的末尾添加了以下行,以查看“实时”大写锁定状态:

displayArea.append("Caps Lock: " +
    Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK) +
    newline);

如果您对VK_SCROLL_LOCK 执行相同操作,您应该会看到按下键时滚动锁定状态打开和关闭。

每当您调用getLockingKeyState() 时,您都会获得该键的当前“实时”状态,因此只要您需要知道该键的状态,只需调用该方法即可。

【讨论】:

我不愿意听按键...我只想使用 getLockingKeyState() 实时滚动锁定状态.. 无论何时调用该方法,您都应该获得滚动锁定键的“实时”状态。如果这不是您所看到的,MCVE 将帮助其他人调试您的问题。 我在问题中提供了一个最小的代码来重现问题.. 啊,有道理。如果您正在运行 GUI 并拥有应用程序焦点,您只会看到对 setLockingKeyState() 的更新。 查看例如这些库:github.com/kwhat/jnativehook 和 github.com/kristian/system-hook 如果您没有 Java AWT Frame 处于焦点/在前台运行,则作为潜在的解决方案【参考方案3】:

实际上,我遇到过类似的情况,我通过使用Robot. 解决了当您想知道滚动锁定是否打开并获取其正确状态时,让机器人按下/释放按钮 2 次。但是,(可能是一个错误左右),如果您立即执行此操作,则状态不正确。为了获得正确的状态,您将不得不添加一个延迟(人类无法察觉)。这意味着,您必须在后台线程中执行此操作,而不是在 EDT 中,因为这会导致 EDT 冻结(当线程休眠时,事件无法发生)。

请参阅此示例(代码中的一些 cmets)。即使窗口不在焦点上,它也总是给出正确的滚动锁定状态。此外,还有一个全局键监听器,因此您知道是否按下了滚动锁(但仅在窗口具有焦点时触发)。

public class ScrollLockDetection 
    private static final int SCROLL_LOCK = KeyEvent.VK_SCROLL_LOCK;
    private JFrame frame;

    public ScrollLockDetection() 
        frame = new JFrame();
        frame.setSize(400, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addWindowFocusListener(new WindowAdapter() 

            @Override
            public void windowGainedFocus(WindowEvent e) 
                showScrollLockStatus();
            
        );
        registerGlobalScrollLockListener();
        frame.setVisible(true);

    

    private void registerGlobalScrollLockListener() 
        Toolkit.getDefaultToolkit().addAWTEventListener(event -> 
            if (event instanceof KeyEvent) 
                KeyEvent keyEvent = (KeyEvent) event;
                if (keyEvent.getID() == KeyEvent.KEY_RELEASED && keyEvent.getKeyCode() == KeyEvent.VK_SCROLL_LOCK) 
                    showScrollLockStatus();
                
            
        , AWTEvent.KEY_EVENT_MASK);
    

    private void showScrollLockStatus() 
        ScrollLockDetector scrollLockDetector = new ScrollLockDetector(b -> 
            System.out.println("Scroll lock ON: " + b);
        );
        scrollLockDetector.execute();
    

    class ScrollLockDetector extends SwingWorker<Boolean, Void> 
        private Consumer<Boolean> consumer;

        public ScrollLockDetector(Consumer<Boolean> consumer) 
            this.consumer = consumer;
        

        @Override
        protected Boolean doInBackground() throws Exception 
            //First we have to remove all global key listeners so the robot does not fire them
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            AWTEventListener[] globalKeyListeners = toolkit.getAWTEventListeners(AWTEvent.KEY_EVENT_MASK);
            while (toolkit.getAWTEventListeners(AWTEvent.KEY_EVENT_MASK).length > 0)
                toolkit.removeAWTEventListener(toolkit.getAWTEventListeners(AWTEvent.KEY_EVENT_MASK)[0]);

            Robot robot = new Robot();
            robot.keyPress(SCROLL_LOCK);
            robot.keyRelease(SCROLL_LOCK);
            Thread.sleep(3);
            robot.keyPress(SCROLL_LOCK);
            robot.keyRelease(SCROLL_LOCK);
            Thread.sleep(3);
            //Re-add the global key listeners
            Stream.of(globalKeyListeners).forEach(listener -> toolkit.addAWTEventListener(listener, AWTEvent.KEY_EVENT_MASK));
            return toolkit.getLockingKeyState(SCROLL_LOCK);
        

        @Override
        protected void done() 
            try 
                Boolean isScrollLockOn = get();
                consumer.accept(isScrollLockOn);
             catch (InterruptedException | ExecutionException e) 
                e.printStackTrace();
            
        
    

    public static void main(String[] args) 
        SwingUtilities.invokeLater(() -> 
            new ScrollLockDetection();
        );
    


在 cmets 中声明之后(它们应该存在问题),实现滚动锁定是否开启/关闭的最佳方法是使用a global key listener。当您的应用程序启动时,它会从getLockingKeyState 获取滚动锁定状态,然后每次按下滚动锁定时,您都会更改它。

查看这个完整的例子:

public class GlobalKeyListenerExample implements NativeKeyListener 
    private static boolean scrollLock;

    @Override
    public void nativeKeyPressed(NativeKeyEvent e) 
    

    @Override
    public void nativeKeyReleased(NativeKeyEvent e) 
        if (e.getKeyCode() == NativeKeyEvent.VC_SCROLL_LOCK) 
            scrollLock = !scrollLock;
            System.out.println("Scroll lock is:" + (scrollLock ? "ON" : "OFF"));
        
    

    @Override
    public void nativeKeyTyped(NativeKeyEvent e) 
    

    public static void main(String[] args) 
        try 
            scrollLock = Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_SCROLL_LOCK);
            System.out.println("Initial state of scrollock: " + (scrollLock ? "ON" : "OFF"));
            GlobalScreen.registerNativeHook();
            Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName());
            logger.setLevel(Level.WARNING);

            // Don't forget to disable the parent handlers.
            logger.setUseParentHandlers(false);
            // Don't forget to disable the parent handlers.
         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());
    

现在,要检查滚动锁定是否打开,您只需检查 scrollLock 变量。

【讨论】:

@Shoaeb 你能解释一下“有效”是什么意思吗?当窗口不在焦点上并且您按下滚动锁定时,什么也没有发生(您想要发生什么吗?)但是当您将焦点移回窗口时,它会检测到它。 如果正在监听 scoll lock 键的窗口不在焦点上,它将无法获得滚动锁定状态...假设我的要求是读取滚动锁定 LED在键盘上... @Shoaeb 但是当您获得焦点时,它会检测到它。你能告诉我你想达到什么目标吗?如果用户不在您的应用程序中,那么知道滚动锁定是否打开的原因是什么? 我有一个应用程序将目录从源复制到目标...现在我想编写一个代码,如果滚动锁定打开,则在后台执行复制,如果不打开则不会复制..

以上是关于尝试使用 JAVA 读取 Scroll Lock 状态的主要内容,如果未能解决你的问题,请参考以下文章

(Arduino Uno重写为USB HID)如何发送Scroll Lock键?

打开或关闭Scroll Lock

滚动锁定 scroll lock 键有什么用?

饿了么插件infinite-scroll

android中的scrollView问题

better-scroll踩坑合集