当前位置: 首页 > 面试题库 >

Java 如何使用键绑定代替键侦听器

竺焕
2023-03-14
问题内容

KeyListener在代码(游戏或其他方式)中使用s作为屏幕对象对用户键输入做出反应的方式。这是我的代码:

public class MyGame extends JFrame {

    static int up = KeyEvent.VK_UP;
    static int right = KeyEvent.VK_RIGHT;
    static int down = KeyEvent.VK_DOWN;
    static int left = KeyEvent.VK_LEFT;
    static int fire = KeyEvent.VK_Q;

    public MyGame() {

//      Do all the layout management and what not...
        JLabel obj1 = new JLabel();
        JLabel obj2 = new JLabel();
        obj1.addKeyListener(new MyKeyListener());
        obj2.addKeyListener(new MyKeyListener());
        add(obj1);
        add(obj2);
//      Do other GUI things...
    }

    static void move(int direction, Object source) {

        // do something
    }

    static void fire(Object source) {

        // do something
    }

    static void rebindKey(int newKey, String oldKey) {

//      Depends on your GUI implementation.
//      Detecting the new key by a KeyListener is the way to go this time.
        if (oldKey.equals("up"))
            up = newKey;
        if (oldKey.equals("down"))
            down = newKey;
//      ...
    }

    public static void main(String[] args) {

        new MyGame();
    }

    private static class MyKeyListener extends KeyAdapter {

        @Override
        public void keyPressed(KeyEvent e) {

            Object source = e.getSource();
            int action = e.getExtendedKeyCode();

/* Will not work if you want to allow rebinding keys since case variables must be constants.
            switch (action) {
                case up:
                    move(1, source);
                case right:
                    move(2, source);
                case down:
                    move(3, source);
                case left:
                    move(4, source);
                case fire:
                    fire(source);
                ...
            }
*/
            if (action == up)
                move(1, source);
            else if (action == right)
                move(2, source);
            else if (action == down)
                move(3, source);
            else if (action == left)
                move(4, source);
            else if (action == fire)
                fire(source);
        }
    }
}

我的响应能力有问题:

  • 我需要单击该对象才能使其工作。
  • 我对按下其中一个键的反应不是我希望它如何工作-反应太灵敏或反应迟钝。
    为什么会发生这种情况,我该如何解决?

问题答案:

该答案说明并演示了如何使用键绑定代替键侦听器来进行教学。它不是

  • 如何用Java编写游戏。
  • 良好的代码编写外观应该如何(例如可见性)。
  • 实现键绑定的最有效的方法(基于性能或代码)。

它是

对于那些在关键听众方面遇到麻烦的人,我要发表的答案是。
回答; 阅读有关键绑定的Swing教程。

我不想阅读手册,告诉我为什么我想使用键绑定而不是已经拥有的漂亮代码!

好吧,Swing教程说明了

  • 按键绑定不需要你单击组件(使其具有焦点):
  • 从用户的角度消除意外行为。
  • 如果你有2个对象,则它们将无法同时移动,因为在给定时间只有一个对象可以具有焦点(即使将它们绑定到不同的键)也是如此。
  • 键绑定更易于维护和操作:
  • 禁用,重新绑定,重新分配用户操作要容易得多。
  • 该代码更易于阅读。

  • 好的,你说服了我尝试一下。它是如何工作的?

键绑定涉及2个对象InputMap和ActionMap。InputMap将用户输入映射到动作名称,ActionMap将动作名称映射到Action。当用户按下某个键时,将在输入映射图中搜索该键并找到一个动作名称,然后在该动作图中搜索该动作名称并执行该动作。

  1. 看起来很麻烦。为什么不将用户输入直接绑定到操作并删除操作名称?然后,你只需要一张地图,而不需要两张。

好问题!你将看到,这是使键绑定更易于管理(禁用,重新绑定等)的事情之一。

  • 我希望你给我一个完整的工作代码。

否(Swing教程中有工作示例)。

You suck! I hate you!

以下是进行单个键绑定的方法:

myComponent.getInputMap().put("userInput", "myAction");
myComponent.getActionMap().put("myAction", action);

请注意,有3 InputMap秒对不同的聚焦状态做出反应:

myComponent.getInputMap(JComponent.WHEN_FOCUSED);
myComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
myComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
  • WHEN_FOCUSED,也就是没有提供任何参数时使用的组件,当组件具有焦点时使用。这类似于关键侦听器的情况。
  • WHEN_ANCESTOR_OF_FOCUSED_COMPONENT当焦点组件位于已注册以接收操作的组件内部时,使用。如果你在太空飞船中有很多机组人员,并且你希望太空飞船在任何机组人员都集中注意力的情况下继续接收输入,请使用此功能。
  • WHEN_IN_FOCUSED_WINDOW当注册用于接收动作的组件位于焦点组件内部时使用。如果你在一个集中的窗口中有许多坦克,并且希望所有坦克同时接收输入,请使用此功能。
    假设两个对象要同时被控制,问题中呈现的代码将类似于以下内容:
public class MyGame extends JFrame {

    private static final int IFW = JComponent.WHEN_IN_FOCUSED_WINDOW;
    private static final String MOVE_UP = "move up";
    private static final String MOVE_DOWN = "move down";
    private static final String FIRE = "move fire";

    static JLabel obj1 = new JLabel();
    static JLabel obj2 = new JLabel();

    public MyGame() {

//      Do all the layout management and what not...

        obj1.getInputMap(IFW).put(KeyStroke.getKeyStroke("UP"), MOVE_UP);
        obj1.getInputMap(IFW).put(KeyStroke.getKeyStroke("DOWN"), MOVE_DOWN);
//      ...
        obj1.getInputMap(IFW).put(KeyStroke.getKeyStroke("control CONTROL"), FIRE);
        obj2.getInputMap(IFW).put(KeyStroke.getKeyStroke("W"), MOVE_UP);
        obj2.getInputMap(IFW).put(KeyStroke.getKeyStroke("S"), MOVE_DOWN);
//      ...
        obj2.getInputMap(IFW).put(KeyStroke.getKeyStroke("T"), FIRE);

        obj1.getActionMap().put(MOVE_UP, new MoveAction(1, 1));
        obj1.getActionMap().put(MOVE_DOWN, new MoveAction(2, 1));
//      ...
        obj1.getActionMap().put(FIRE, new FireAction(1));
        obj2.getActionMap().put(MOVE_UP, new MoveAction(1, 2));
        obj2.getActionMap().put(MOVE_DOWN, new MoveAction(2, 2));
//      ...
        obj2.getActionMap().put(FIRE, new FireAction(2));

//      In practice you would probably create your own objects instead of the JLabels.
//      Then you can create a convenience method obj.inputMapPut(String ks, String a)
//      equivalent to obj.getInputMap(IFW).put(KeyStroke.getKeyStroke(ks), a);
//      and something similar for the action map.

        add(obj1);
        add(obj2);
//      Do other GUI things...
    }

    static void rebindKey(KeyEvent ke, String oldKey) {

//      Depends on your GUI implementation.
//      Detecting the new key by a KeyListener is the way to go this time.
        obj1.getInputMap(IFW).remove(KeyStroke.getKeyStroke(oldKey));
//      Removing can also be done by assigning the action name "none".
        obj1.getInputMap(IFW).put(KeyStroke.getKeyStrokeForEvent(ke),
                 obj1.getInputMap(IFW).get(KeyStroke.getKeyStroke(oldKey)));
//      You can drop the remove action if you want a secondary key for the action.
    }

    public static void main(String[] args) {

        new MyGame();
    }

    private class MoveAction extends AbstractAction {

        int direction;
        int player;

        MoveAction(int direction, int player) {

            this.direction = direction;
            this.player = player;
        }

        @Override
        public void actionPerformed(ActionEvent e) {

            // Same as the move method in the question code.
            // Player can be detected by e.getSource() instead and call its own move method.
        }
    }

    private class FireAction extends AbstractAction {

        int player;

        FireAction(int player) {

            this.player = player;
        }

        @Override
        public void actionPerformed(ActionEvent e) {

            // Same as the fire method in the question code.
            // Player can be detected by e.getSource() instead, and call its own fire method.
            // If so then remove the constructor.
        }
    }
}

你可以看到,将输入映射与动作映射分开可以实现可重用的代码并更好地控制绑定。此外,如果需要功能,也可以直接控制操作。例如:

FireAction p1Fire = new FireAction(1);
p1Fire.setEnabled(false); // Disable the action (for both players in this case).

有关更多信息,请参见“ 操作”教程。

我看到你对4个键(方向)使用了1个动作(移动),对1个键使用了1个动作(触发)。为什么不给每个键一个自己的动作,或者不给所有键一个相同的动作,并理清在该动作内部做什么(例如在移动情况下)?

好点子。从技术上讲,你可以同时进行这两种操作,但是你必须考虑什么才是有意义的,以及什么才能简化管理和重用代码。在这里,我假设在所有方向上的移动都是相似的,而发射是不同的,所以我选择了这种方法。

我看到很多KeyStroke用过的,那是什么?他们像KeyEvent吗?

是的,它们具有类似的功能,但更适合在此使用。请参阅其API以获取信息以及如何创建它们。

有什么问题吗 有改善吗?有什么建议吗?发表评论。有更好的答案吗?发表它。



 类似资料:
  • 问题内容: 我正在尝试添加一个包含一个的键侦听器。 当收到ctrl + tab时,应该切换标签。 但是按键事件从未发送过,我尝试将其添加到面板和选项卡式对象中,但是没有成功。 这是我的代码 问题答案: 通常,正确的Swing组件不会拦截您的按键事件。您必须了解,光标下方的第一个组件将收到键盘事件。如果您使用键盘选择一个按钮,则将是这个JButton接收按键事件。 为了确保获得所有这些事件,您不必在

  • 问题内容: 我需要绑定所有箭头键以执行相同的功能,但是每次获取按下哪个键时。目前我只有通过以下方式按下向右箭头键时才有 但是我需要这样的东西 问题答案: 您要问的实际上是违反直觉的,并且违背了键绑定API的设计。 目的是为每个按键提供单个工作单元。在我看来,这建议您应为每个箭头键分别执行操作。 它使您更容易遵循逻辑,进行更改,根据需要规避操作。 但是我是谁呢? 如果看不到它,一种简单的方法就是为每

  • 问题内容: 我试图写一个计算器,有一个问题。我已经为所有按钮制作了动作监听器,现在我希望可以从键盘输入数据。我是否需要对KeyListener或Keybinding进行全部操作,还是有其他方法可以使单击按钮后将其发送到actionlistener中的说明?还有更好的方法:Keylistener或Keybinding 问题答案: 一般而言,在您的键输入集有限的情况下,键绑定是一个更好的选择。 遭受与

  • 问题内容: 我正在尝试在JPanel上的Java中进行键绑定。我希望在按下“ w”按钮时执行某些操作。我遵循Java教程进行绑定,但是actionPerformed方法不执行(即,没有文本打印出来)。以下是我的测试GUI的全部代码,并突出显示了相关部分: 永远不会打印文本“ test”。我已经尝试了很多次,使用了许多不同的变体,不同的键,并且确保面板聚焦,但没有运气。我究竟做错了什么? 问题答案:

  • 替代键充当了每个实体实例主键之外的备用唯一关键字。替代键可用于指定关系。在使用关系数据库的时候,替代键映射为备用关键字列上的唯一索引/约束这一概念,一个或者多个外键约束将引用这个(这些)列。 提示 如果你只想要实施某个列的唯一性,那么你想要的应该是唯一索引,而不是替代键,请查看 索引。在 EF 中,替代键比索引提供了更丰富的功能,因为它们可以用作外键的目标。 替代键通常在你需要的时候才被引入,并且

  • 我想知道如何在一个键事件中按下所有的键。例如,我想为Ctrl+F编写一个监听器,它可以切换全屏。如何检查在一个事件中是否同时按下了Ctrl和F?