SWT 模拟键盘事件

导言

    在GUI设计中,我们往往要模拟鼠标和键盘事件,在java 的awt 中的java.awt.Robot; 类就可以进行模拟。但是在SWT中,我尝试了一下,会抛出空指针异常,或许是我写得有问题,或许是SWT不能用Robot类来模拟,这个我没有进一步去验证,感兴趣的同志们可以去尝试一下。
    下面来说说怎么在SWT中模拟键盘按键的按下。

思路

    实现一个Runnable接口SimulateButtonDown去生成一个KeyEevnet,然后发送出去给KeyAdapeter进行捕获处理。

验证

    为了验证上述方案是否可行,我写了一个只有两个按钮的简单GUI窗口。
    
SimulateButtonDown类:

package com.lks.test;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;

public class SimulateButtonDown implements Runnable {

    private char ch;
    private Display display;

    public SimulateButtonDown(char ch, Display display) {
        this.ch = ch;
        this.display = display;
    }

    /* * (非 Javadoc) * * @see java.lang.Runnable#run() */
    @Override
    public void run() {

        Event e = new Event();
        System.out.println("before run : " + this.ch);//查看修改字符
        e.type = SWT.KeyDown;//修改事件类型
        e.character = this.ch;//修改相应的字符
        System.out.println("After run : " + e.character);//查看是否被正确赋值
        display.post(e);
    }

}

Windows类:

package com.lks.test;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

public class Windows {

    protected Shell shlTest;
    private Button btnLeft;
    private Button btnRight;
    private Display display;

    /** * Launch the application. * @param args */
    public static void main(String[] args) {
        try {
            Windows window = new Windows();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** * Open the window. */
    public void open() {
        display = Display.getDefault();
        createContents();
        shlTest.open();
        shlTest.layout();
        while (!shlTest.isDisposed()) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    /** * Create contents of the window. */
    protected void createContents() {
        shlTest = new Shell();
        shlTest.setSize(231, 297);
        shlTest.setText("Test");

        btnLeft = new Button(shlTest, SWT.NONE);

        btnLeft.setBounds(10, 41, 80, 27);
        btnLeft.setText("+");
        btnLeft.addSelectionListener(new ButtonClickListener("+"));
        btnLeft.addKeyListener(new KeyListener());
        btnRight = new Button(shlTest, SWT.NONE);
        btnRight.setText("-");
        btnRight.setBounds(96, 41, 80, 27);
        btnRight.addSelectionListener(new ButtonClickListener("-"));
        btnRight.addKeyListener(new KeyListener());
    }

    private final class ButtonClickListener extends SelectionAdapter {
        private String btnName;
        public ButtonClickListener(String btnName) {
            this.btnName = btnName;
        }
        @Override
        public void widgetSelected(SelectionEvent e) {
            if ("+".equals(btnName)) {
                new Thread(new SimulateButtonDown('+', display)).start();
            }
            else if ("-".equals(btnName)) {
                new Thread(new SimulateButtonDown('-', display)).start();
            }
        }
    }

    private final class KeyListener extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            System.out.println("keyListener: " + e.character);  
        }
    }

}

窗口如下:

点击按钮-,运行结果如下:

before run : -
After run : -
keyListener: -

可见Keylistner可以成功监听到我们生成的事件,我们继续点按钮+,看看会发生什么

我们传进去“+”号,出来的是“=”号!!!What?惊不惊喜?意不意外?刺不刺激?!
我们在SimulateButtonDown接口类中将run方法修改成下面这样子,看看我们生成的keyCode是什么情况

@Override
    public void run() {

        Event e = new Event();

        System.out.println("before run : " + this.ch + " " + e.keyCode);
        e.type = SWT.KeyDown;
        e.character = this.ch;
        System.out.println("run : " + this.ch + " " + e.keyCode);
        System.out.println("After run : " + e.character + " " + e.keyCode);

        display.post(e);
    }

在内部类KeyListener中修改一下keyPressed方法:

@Override
        public void keyPressed(KeyEvent e) {
            System.out.println("keyListener: " + e.character + " " + e.keyCode);    
        }

再次运行程序:

before run : - 0
run : - 0
After run : - 0
keyListener: - 45
before run : + 0
run : + 0
After run : + 0
keyListener: = 61

然后直接按键盘的+和-看看keyCode:

keyListener: + 16777259
keyListener: - 16777261

由此可见,我们的keyCode并没有被改变,可是明明character已经变了,可为什么还不行呢?这个我也不清楚原因,知道原因的朋友可以给我留个言,非常感谢。

那我们试试直接修改keyCode能不能正常工作呢?实践是检验真理的唯一标准~
将SimulateButtonDown接口类修改成:

package com.lks.test;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;

public class SimulateButtonDown implements Runnable {

    private int keyCode;
    private Display display;

    public SimulateButtonDown(int keyCode, Display display) {
        this.keyCode = keyCode;
        this.display = display;
    }

    /* * (非 Javadoc) * * @see java.lang.Runnable#run() */
    @Override
    public void run() {

        Event e = new Event();

        System.out.println("before run : " + e.keyCode);
        e.type = SWT.KeyDown;
        e.keyCode = this.keyCode;
        System.out.println("After run : "  + e.keyCode);
        display.post(e);
    }

}

在ButtonClickListener内部类中修改相应的代码:

@Override
        public void widgetSelected(SelectionEvent e) {
            if ("+".equals(btnName)) {
                new Thread(new SimulateButtonDown(SWT.KEYPAD_ADD, display)).start();
            }
            else if ("-".equals(btnName)) {
                new Thread(new SimulateButtonDown(SWT.KEYPAD_SUBTRACT, display)).start();
            }
        }

运行结果如下:

before run : 0
After run : 16777261
keyListener: - 16777261
before run : 0
After run : 16777259
keyListener: + 16777259

由此可见,只修改character不能实现想要的按键模拟,而修改keyCode可以正常工作。