Remapping the Keyboard at a low level

喜欢而已 提交于 2019-12-02 03:36:48

If I were doing this, I would write the Java App without worrying about the key bindings. Assume that when a component gets a keyevent for #7 its #7, don't worry about whether the 7 or 1 was really typed. The app shouldn't care about how keys are mapped on the keyboard. This should let you start developing the app immediately.

As far as overriding key bindings, this seems like where you want to look: http://download.oracle.com/javase/7/docs/api/java/awt/KeyEventDispatcher.html

It sounds like you can write your own KeyEventDispatcher to handle all the key mapping logic and it prevents mapping logic from messing up the rest of the logic in your application.

This is hacky, and I'll admit I haven't used it myself, but you could subclass KeyEvent and override the fields as needed. So yourSubclass.VK_NUMPAD1 is the integer value of KeyEvent.VK_NUMPAD7, yourSubclass.VK_NUMPAD2 is the integer value of KeyEvent.VK_NUMPAD8, etcetera. Then use your subclass everywhere KeyEvent is normally used.

I also agree that the KeyEventDispatcher should work. But if this a version/platform issue, then maybe you can use a custom Event Queue. See Global Event Dispatching

My stack overfloweth! The answer was here: http://download.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html which says:

For key pressed and key released events, the getKeyCode method returns the event's keyCode. For key typed events, the getKeyCode method always returns VK_UNDEFINED.

My original attempt thought it could get a keyCode on KEY_TYPED. It could not, and it was the KEY_TYPED event that was clobbering the mapping done in KEY_PRESSED.

Here is a working implementation:

import static java.awt.event.KeyEvent.*;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;


public class KeyboardDispatcherDemo extends JFrame {

    /**
     * This class shows how to map numeric keypad keys.
     * It performs two conversions:
     * 1. lower-to-uppercase
     * 2. if numeric keypad 7-9 is typed, 1-3 appears and vice versa.
     * 
     * This is modified from the code at 
     * http://www.exampledepot.com/egs/java.awt/DispatchKey.html#comment-51807
     * which demoes the lower-to-upper conversion.
     * 
     * It doesn't yet handle modified numeric keypad keys.
     * 
     */
    public KeyboardDispatcherDemo() {

        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
            new KeyEventDispatcher() {
                private char lastMappedKey;
                private final Map<Integer, Character> keyMap = 
                    new HashMap<Integer, Character>() { 
                {
                    put(VK_NUMPAD1, '7'); 
                    put(VK_NUMPAD2, '8'); 
                    put(VK_NUMPAD3, '9'); 
                    put(VK_NUMPAD7, '1'); 
                    put(VK_NUMPAD8, '2'); 
                    put(VK_NUMPAD9, '3'); 

                }};

                public boolean dispatchKeyEvent(KeyEvent e) {
                    System.out.println(String.format("INPUT: %s", e.toString()));
                    boolean dispatch = false;
                    switch (e.getID()) {
                    case KeyEvent.KEY_PRESSED:
                        dispatch = dispatchKeyPressed(e);
                        break;
                    case KeyEvent.KEY_TYPED:
                        dispatch = dispatchKeyTyped(e);
                        break;
                    case KeyEvent.KEY_RELEASED: 
                        dispatch = dispatchKeyReleased(e);
                        break;
                    default:
                        throw new IllegalArgumentException();
                    }
                    System.out.println(String.format("OUTPUT: %s", e.toString()));
                    System.out.println();
                    return dispatch;
                }
                private boolean dispatchKeyPressed(KeyEvent e) {
                    char k = e.getKeyChar();
                    if (k != CHAR_UNDEFINED) {
                        if (Character.isLetter(k)) {
                            e.setKeyChar(Character.toUpperCase(e.getKeyChar()));
                        } else if (e.getModifiers() == 0){
                            Character mapping = keyMap.get(e.getKeyCode());
                            if (mapping != null) {
                                e.setKeyChar(mapping);
                            }
                        }
                        // save the last mapping so that KEY_TYPED can use it.
                        // note we don't do this for modifier keys.
                        this.lastMappedKey = e.getKeyChar();
                    }
                    return false;
                }

                // KEY_TYPED events don't have keyCodes so we rely on the
                // lastMappedKey that was saved on KeyPressed.
                private boolean dispatchKeyTyped(KeyEvent e) {
                    char k = e.getKeyChar();
                    if (k != CHAR_UNDEFINED) {
                        e.setKeyChar(lastMappedKey);
                    }
                    return false;
                }
                private boolean dispatchKeyReleased(KeyEvent e) {
                    char k = e.getKeyChar();
                    if (k != CHAR_UNDEFINED) {
                        e.setKeyChar(lastMappedKey);
                        this.lastMappedKey=CHAR_UNDEFINED;
                    }
                    return false;

                }
            });




        setTitle("KeyboardDispatcherDemo");
        JPanel panel = new JPanel();
        panel.setBackground(new Color(204, 153, 255));
        panel.setLayout(new BorderLayout());
        getContentPane().add(panel, BorderLayout.CENTER);

        JTextArea staticText = new JTextArea();
        staticText.setText("This demonstrates how to map numeric keypad keys.  It uppercases all letters and converts Numeric Keypad 1-3 to 7-9 and vice versa.  Try it.");
        staticText.setLineWrap(true);
        staticText.setWrapStyleWord(true);
        panel.add(staticText, BorderLayout.NORTH);
        staticText.setFocusable(false);

        JTextField textField = new JTextField();
        textField.setText("");
        textField.setHorizontalAlignment(SwingConstants.LEFT);
        panel.add(textField, BorderLayout.SOUTH);
        textField.setColumns(10);
        textField.setFocusable(true);


        setSize(getPreferredSize());

        setVisible(true);


    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        new KeyboardDispatcherDemo();

    }

    @Override
    public Dimension getPreferredSize() {
        // TODO Auto-generated method stub
        return new Dimension(400,300);
    }

}

Thanks to all who nudged me toward the answer.

which brings me to my next question ... stay tuned.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!