Action和ActionMap - 解释这个行为给我。

3
我有一个 Action
SampleAction a = new SampleAction("foo", null);

然后我将它添加到一个按钮和ActionMap中。
JButton b = new JButton(a);
b.getActionMap().put("bar", a);
b.getInputMap().put(KeyStroke.getKeyStroke("F1"), "bar");

我在Action中添加了一个跟踪代码(System.out.println("Action [" + e.getActionCommand() + "] performed!");)。当我用鼠标点击按钮时,它显示如下:

Action [foo] performed!

但是当我使用F1键时,它会显示:
Action [null] performed!

为什么?


class SampleAction extends AbstractAction
{
    public SampleAction(String text, Icon icon) {
        super(text, icon);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Action [" + e.getActionCommand() + "] performed!");
    }
}

我们能否看到SimpleAction类,发布一个SSCCE - David Kroukamp
1
@DavidKroukamp,这是给你的。 - Fabricio
为什么问题很难回答(无法看到Swing团队开发人员的大脑,甚至不在现场,更不用说过去了 :-) 如果一个Action没有明确设置它的actionCommand,则默认为SomeThing。在创建actionEvent的两个位置(在AbstractButton.fireActionEvent与SwingUtilities.notifyAction中),该SomeThing是不同的。 - kleopatra
2个回答

3
除非我误解了,否则您应该通过 ae.getSource()JButton 实例上调用 getActionCommand,而不是在 ActionEvent 上调用 getActionCommand()
  SampleAction a = new SampleAction("foo", null);

  JButton b = new JButton(a);
  b.getActionMap().put("bar", a);
  b.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F1"), "bar");

class SampleAction extends AbstractAction
{
    public SampleAction(String text, Icon icon) {
    super(text, icon);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
    System.out.println("Action [" + ((JButton)e.getSource()).getActionCommand() + "] performed!");
    }
}

更新:

感谢@Kleopatra,这可能是一个更好的方法:

SampleAction a = new SampleAction("foo", null);

JButton b = new JButton(a);
b.getActionMap().put("bar", a);
b.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F1"), "bar");

 class SampleAction extends AbstractAction {

        public SampleAction(String text, Icon icon) {
            super(text, icon);

            putValue(Action.ACTION_COMMAND_KEY, text);//'foo' will be printed when button clicekd/F1 pressed
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Action [" + e.getActionCommand() + "] performed!");
        }
    }

嗯...那并没有真正回答问题(至少我是这样认为的 :-))- 它更多地涉及当相同的Action用作按钮(在点击时执行)与用于actionMap时,_actionCommand_的区别。 - kleopatra
我使用了JComponent.WHEN_IN_FOCUSED_WINDOW,但是当按下F1键时仍然打印出null - Fabricio
1
哦,现在是 :P 我只是以为我在按键绑定方面做错了什么。:) 但后来看起来没问题。 - Fabricio
1
问题被转化了,又是那种“不要这样做”的情况,恐怕让人失望。按钮内部默认创建的 actionCommand 是一项未经记录的实现细节(本身并不是最聪明的东西:名称可能会本地化和更改,不是一个很好的标识符候选)。需要命令的应用程序代码 不能 依赖这样一个复杂的属性。相反,在_action 上设置它,这样就达到了 Action 的整个目的:设计为独立于视图使用(可能是 JTextField 或其他任何东西)。 - kleopatra
@kleopatra,我知道的另一种解决方案是使用助记符,但这将强制使用ALT +键序列。 - David Kroukamp
1
嗯...我的Action.putValue(Action.ACTION_COMMAND_KEY, "fooCommand")有什么问题吗? - kleopatra

1

我无法访问您的SampleAction,但我猜测您在构造函数中传递的“foo”文本仅用作文本,与操作命令无关。

如果您查看JButton扩展自AbstractButton类,则会发现

public String getActionCommand() {
    String ac = getModel().getActionCommand();
    if(ac == null) {
        ac = getText();
    }
    return ac;
}

在创建传递给操作的ActionEvent时使用此方法。当您单击按钮时,将调用此方法,并且我假设acnull,但getText()方法返回您在SampleAction类中使用的"foo"

如果直接按F1触发操作,则会绕过此机制并简单地触发操作。如果要避免这种情况,可以将Action添加到JButtonActionMap中,该Action只需执行JButton#doClick,这是执行“单击”按钮的API调用。


哦,那不重要。我只是以为我在按键绑定方面做错了什么。 :) - Fabricio
1
“然而...”这个词有点误导人:没有绕过任何东西,只是在不同的位置创建了ActionEvent,请参见SwingUtilities.notifyAction :) 默认值只是...默认值,并且无法控制(甚至未记录在案)。如果我们需要控制它们,唯一的出路就是使用配置操作。 - kleopatra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接