区分虚拟(机器人)和物理 KeyEvents

Differentiating Between Virtual (Robot) and Physical KeyEvents

我想知道是否有区分 KeyEvent 对象的虚拟(来自 AWT 机器人)和物理源的合适方法?

我正在制作虚拟键盘,但希望键盘消失when/if用户使用物理键盘。

下面的示例说明了我需要解决的问题(同时忽略了实现实际虚拟键盘的其他问题)。我正在尝试修复 FIXME 行下方的 if/else 语句(我意识到那里的逻辑不正确)。

import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class RobotTest
{
    public static void main( String[] args )
    {
        new RobotTest();
    }

    public RobotTest()
    {
        // Create a "virtual keyboard"
        MyWindow window = new MyWindow();
        window.setVisible( true );

        // Event listener to differentiate between virtual and physical key events.
        Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
        {
            @Override
            public void eventDispatched( final AWTEvent e )
            {
                if (( e instanceof KeyEvent )
                        && ( ( KeyEvent ) e ).getID()==KeyEvent.KEY_PRESSED
                        && ( ( KeyEvent ) e ).getKeyCode()==KeyEvent.VK_A)
                {
                    // FIXME: BELOW IS GUARANTEED FALSE
                    if ( ( ( KeyEvent ) e ).getSource() instanceof Robot)
                    {
                        System.out.println("FROM ROBOT");
                    }
                    else
                    {
                        System.out.println("FROM KEYBOARD");
                    }
                }
            }
        }, AWTEvent.KEY_EVENT_MASK);
    }

    // prototype keyboard with an "A" key.
    private class MyWindow extends JFrame
    {
        public MyWindow()
        {
            JPanel content = new JPanel();
            content.setLayout( new BorderLayout() );

            // Button that emulates pressing A
            JButton button = new JButton( "A" );
            button.addActionListener( new ActionListener()
            {
                @Override
                public void actionPerformed( ActionEvent e )
                {
                    try
                    {
                        Robot r = new Robot();
                        r.keyPress( KeyEvent.VK_A );
                        r.keyRelease( KeyEvent.VK_A );
                    }
                    catch( AWTException ex )
                    {
                        ex.printStackTrace();
                    }
                }
            });

            content.add( button, BorderLayout.CENTER );
            setContentPane( content );

            setSize(50, 50);
        }
    }
}

因此,我最终采用的路线并不能保证解决确定 KeyEvent 是来自机器人还是物理源的一般问题,但它可以让我检测是否有机器人外部的任何输入.

我只是做了一个机器人预期事件的队列。收到事件后,我会通过轮询队列来检查机器人是否打算按下该键。这几乎可以保证检测到是否存在物理事件,但由于同步性问题,不能保证找到 KeyEvent 的来源。我已经附上了(非常简单的)解决方案,以防它对任何人都有用。

import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.LinkedList;
import java.util.Queue;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class RobotTest
{
    // CHANGE #1: store a queue of virtual key presses.
    public static Queue<Integer> robotKeys = new LinkedList<Integer>();


    public static void main( String[] args )
    {
        new RobotTest();
    }


    public RobotTest()
    {
        // Create a "virtual keyboard"
        MyWindow window = new MyWindow();
        window.setVisible( true );

        // Event listener to differentiate between virtual and physical key events.
        Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
        {
            @Override
            public void eventDispatched( final AWTEvent e )
            {
                if (( e instanceof KeyEvent )
                        && ( ( KeyEvent ) e ).getID()==KeyEvent.KEY_PRESSED
                        && ( ( KeyEvent ) e ).getKeyCode()==KeyEvent.VK_A)
                {
                    // CHANGE #2: check the received event against the front of the queue.
                    Integer expectedRobotKey = robotKeys.poll();
                    if (expectedRobotKey != null && expectedRobotKey == ( ( KeyEvent ) e ).getKeyCode())
                    {
                        System.out.println("No physical input detected.");
                    }
                    else
                    {
                        System.out.println("Physical input detected.");
                    }
                }
            }
        }, AWTEvent.KEY_EVENT_MASK);
    }

    // prototype keyboard with an "A" key.
    private class MyWindow extends JFrame
    {
        public MyWindow()
        {
            JPanel content = new JPanel();
            content.setLayout( new BorderLayout() );

            // Button that emulates pressing A
            JButton button = new JButton( "A" );
            button.addActionListener( new ActionListener()
            {
                @Override
                public void actionPerformed( ActionEvent e )
                {
                    try
                    {
                        Robot r = new Robot();
                        // CHANGE #3: add events to the stored queue before sending them out.
                        robotKeys.add( KeyEvent.VK_A );
                        r.keyPress( KeyEvent.VK_A );
                        r.keyRelease( KeyEvent.VK_A );
                    }
                    catch( AWTException ex )
                    {
                        ex.printStackTrace();
                    }
                }
            });

            content.add( button, BorderLayout.CENTER );
            setContentPane( content );

            setSize(50, 50);
        }
    }
}