使用KeyBindings将所有字符映射到操作

时间:2014-06-28 19:24:51

标签: java swing key key-bindings keystroke

我对Swing的使用并不陌生,但我做了大量的工作,而且我正在设计一个新的应用程序。这是一个绘图应用程序。它将允许用户在空白的白色"查看器中的某些位置点击。窗口,使用键盘键入字母和符号,从而在查看器窗口的某处编辑文本。

由于我没有使用JComponent来显示此文本,因此我需要一种可靠的方式让我的应用程序接受输入。我选择使用KeyBindings。但我的特殊查看器组件以空输入映射和空动作映射开始。

我的问题。使用我的键盘将所有字母和符号映射到AbstractAction使用ActionMap和InputMap的最简单方法是什么?操作映射需要使用WHEN_IN_FOCUSED_WINDOW来捕获接口的所有输入。

修改 我以前使用keylistener来做到这一点,但我没有获得模块化 我想要的方式

我删除了代码。把它扔掉了。我相信它在SSCCE中说的是这样的:

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

public class MyKeysHandler extends KeyListener {
//blah blah blah
//blah blah blah
public void keyPressed(KeyEvent ke)
{
// please excuse if the boolean logic of my use of masks is off
// their proper use doesn't come to me easily
// I hope you can get the jist
    if((ke.getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) != 0) { 
        switch(ke.getKeyCode()) {
            // handle capital letters
        case VK_DELETE      : editor.handleThisSpecialKey(ke.getKeyCode);   
        case VK_BACK_SLASH  : // handle back slash
        case VK_7       : // handle the ampersand
        case VK_8       : // handle the asterisk character
        default         : // if just a normal letter...
            editor.handleThisNormalKeyPlease(KeyEvent.getKeyText(ke.getKeyCode()).toUpperCase());
        }
    }
    else {
        switch(ke.getKeyCode()) {
        case VK_DELETE      : // handle the shift-delete command
        case VK_BACK_SLASH  : // handle the question mark
        case VK_7       : // handle the 7
        case VK_8       : // handle the 8
        defualt :
            if(ke.getKeyCode() == VK_C && ke.getModifiersEX() & KeyEvent.CTRL_DOWN_MASK) != 0) {
                editor.handleTheCopyCommandPlease();    
            }   
            else 
                editor.handleThisKeyPlease(KeyEvent.getKeyText(ke.getKeyCode).toLowerCase());
        }
    }       
}
}

但这真的非常麻烦。每次添加密钥时,都必须确保它不会与同一方法中的某些其他密钥代码冲突,并且对于任何应用程序,您必须为每个应用程序创建一个必须包含此细致的密钥。 VK"切换代码。

我的应用程序也会有菜单,我想在有空的时候为那些人安装新的键绑定或加速器(或者他们所称的任何东西 - 助记符?)。为了移动文本并删除它们,只需要几个特殊的组合键就可以做到这一点。至于你在上面看到的关键组合?你可以想象为我做了多少噩梦。

我向你们保证,如果不发布更多代码,我会尝试遵循良好的可重用计算机编程实践的良好模型。我有一个在下面运行并实际处理编辑的模型,以及一个在上面运行并处理菜单按钮代码的视图。

不幸的是,我希望Java不必如上所示那么繁琐。根据你按下的键,我的键盘上的大多数键都不需要响应,并且KeyBindings支持ctrl和shift等组合。

我希望有一个循环解决方案。然后,也许有一个使用WHEN-IN-ANCESTOR的解决方案,也可以工作。我使用的输入映射是组件根窗格,但是我也可以打开到查看器组件本身(一个JPanel)并获取其InputMap实例,但我没有&这样做还没有,因为我正在尝试。

我目前访问项目的方式是:这对我很有用。但它并没有处理符号。也许如果我增加了for循环中的字符范围?还是手动添加几个?我不知道。

import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;
public void initKeyBindingsTheEasyWay() {

JRootPane rootPane = mainPane.getRootPane();
InputMap theWIMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap theAMap = rootPane.getActionMap();
for(char c = 'A'; c <= 'Z'; c++) {
    // lower case   
    KeyStroke little = KeyStroke.getKeyStroke("pressed " + c);
    theWIMap.put(little, ""+Character.toLowerCase(c));
    theAMap.put(""+Character.toLowerCase(c), new LetterAction(Character.toLowerCase(c)));
}
}

public static class LetterAction extends AbstractAction
{
    char theLetter;
    public LetterAction(char letter)
    {
        theLetter = letter;
    }

    public void actionPerformed(ActionEvent ae)
    {
        System.out.print(theLetter);
    }
}

2 个答案:

答案 0 :(得分:1)

大部分键和特殊字符都出现在KeyEvent类中。对于其他字符,您可以通过将char转换为int来获取ASCII表的值。

当你在谈论需要使用WHEN_IN_FOCUSED_WINDOW的ActionMap时,在我看来你没有使用MVC model,你应该考虑它。

答案 1 :(得分:1)

我经过测试和测试,终于找到了解决方案。对于那些感兴趣的人,有两种方法可以解决这个问题。我不会进入涉及搜索TextComponent的API以发现它是如何在那里完成的方式。我试图自己实现这一点非常困难,最终我不得不走另一条路。

我使用了将角色和虚拟键映射到Actions。

这就是我想要的:循环浏览感兴趣的字符的简单方法,从而节省代码和复杂性,只需将按键映射到操作:如果您不关心捕获我在下面列出的某些键只需从正确的数组中删除字符或虚拟键。您甚至可以添加新字符

首先实例化一个JComponent(我在这个例子中使用了一个名为mainPane的JFrame)。我想捕获输入 frame 的所有击键,所以我使用了JComponent.WHEN_IN_FOCUSED_WINDOW。

请注意以下代码中的静态导入KeyEvent常量。

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

mainPane.setFocusTraversalKeysEnabled(false);
JRootPane rootPane = mainPane.getRootPane();
InputMap theWIMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
ActionMap theAMap = rootPane.getActionMap();

然后你可以添加捕获字母击键的代码......

for(char c = 'A'; c <= 'Z'; c++) {
    // upper case
    KeyStroke capital = KeyStroke.getKeyStroke("typed " + c);
    theWIMap.put(capital, Character.toString(c));
    theAMap.put(Character.toString(c), new KeyAction(c));

    // lower case
    KeyStroke little = KeyStroke.getKeyStroke("typed " + Character.toLowerCase(c));
    theWIMap.put(little, Character.toString(Character.toLowerCase(c)));
    theAMap.put(Character.toString(Character.toLowerCase(c)), new KeyAction(Character.toLowerCase(c)));
}

...然后编写代码以捕获许多键盘上常用符号的输入(通过向下看键盘上键盘上的字体可以看到这些键盘。我在这里列出了它们按照Java API SE 7: Constant Field Values中指定的&#34; VK常数值&#34;(?,%,〜和|没有VK常数)。

int[][] characters = new int[][] {
                {'?', '%', '~', '|'},
                {' ', ',', '-', '.', '/'},
                {';', '=', '[', '\\', ']'}, 
                {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'},
                {'*', '+', ',', '-', '.', '/', '&', '*', '\"', '<', '>', '{', '}', '`', '\''},
                {'@', ':', '^', '$', '!', '(', '#', '+', ')', '_'}
                // if you're so inclined: add even more rows to the bottom
                ,{'¡', '€', '\u02ff'}
};

for(int[] range : characters) 
    for(int i = 0; i < range.length; i++) {
        char charForKey = (char)range[i];
        KeyStroke keyboardKey = KeyStroke.getKeyStroke(charForKey);
        theWIMap.put(keyboardKey, charForKey);
        theAMap.put(charForKey, new KeyAction(charForKey));
    }

...最后代码处理在许多键盘上常见的命令键的输入。

    int[][] commands = new int[][] {
        {VK_BACK_SPACE, VK_ENTER, VK_ESCAPE},
        {VK_PAGE_UP, VK_PAGE_DOWN, VK_END, VK_HOME}, 
        {VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN,  VK_DELETE},
        {VK_KP_UP, VK_KP_DOWN, VK_KP_LEFT, VK_KP_RIGHT},
    };
    for(int[] range : commands)
        for(int i = 0; i < range.length; i++) {
            KeyStroke keyboardKey = KeyStroke.getKeyStroke(range[i], 0);
            String commandForKey = KeyEvent.getKeyText(range[i]).toLowerCase();
            theWIMap.put(keyboardKey, commandForKey);
            theAMap.put(commandForKey, new KeyAction(commandForKey));
        }

    theWIMap.put(KeyStroke.getKeyStroke("pressed " + "TAB"), "tab");
    theAMap.put("tab", new KeyAction("tab"));

    theWIMap.put(KeyStroke.getKeyStroke("shift pressed " + "TAB"), "shift tab");
    theAMap.put("shift tab", new KeyAction("shift tab"));       

现在在这里添加此操作代码(忽略编辑器的部分:那是调用我的控制器的部分):

public class KeyAction extends AbstractAction
{
    private static final long serialVersionUID = 1L;
    public char theLetter;
    public String theCommand;
    public enum KeyType {CHARACTER_ENTRY, KEYSTROKE_COMMAND};
    public final KeyType actionType;

    public KeyAction(char letter)
    {
        theLetter = letter;
        actionType = KeyType.CHARACTER_ENTRY;
    }

    public KeyAction(String command)
    {
        theCommand = command;
        actionType = KeyType.KEYSTROKE_COMMAND;
    }


    public void actionPerformed(ActionEvent ae)
    {
        if(actionType == KeyType.CHARACTER_ENTRY) {
            System.out.print(theLetter);
            editor.receiveKey(theLetter);
        }
        else {
            System.out.println("\n" + theCommand);
            editor.receiveCommand(theCommand);
        }

    }
}

打开应用程序的GUI窗口后,输入以下内容:&#34;我喜欢土豆沙拉。 [ENTER]现在捐赠10.00美元![TAB] [SHIFT TAB]&#34; 您应该在终端中看到类似的内容。

I love potato salad.
enter
Donate $10.00 now!
tab

shift tab