如何通过编程将ActionEvent发送到JButton?

15

如何以编程方式向JButton发送ActionEvent(例如按钮按下/ ACTION_PERFORMED)?

我知道有:

button.doClick(0);

并且

button.getModel().setArmed(true);
button.getModel().setPressed(true);
button.getModel().setPressed(false);
button.getModel().setArmed(false);

但是直接发送一个ActionEvent不是也可以吗?

编辑:这不是生产代码,只是个人实验。

5个回答

17
您可以获取按钮的 ActionListener,然后直接调用 actionPerformed 方法。
ActionEvent event;
long when;

when  = System.currentTimeMillis();
event = new ActionEvent(button, ActionEvent.ACTION_PERFORMED, "Anything", when, 0);

for (ActionListener listener : button.getActionListeners()) {
    listener.actionPerformed(event);
}

4
需要注意的一件事是,通常ActionListeners假定它们将在EDT上被调用,因此如果您要手动分派它们,您也需要在EDT上这样做。 - Mark Peters
关于我的评论:有人知道在哪里有记录,可以假定您的ActionListener将在EDT上运行吗? - Mark Peters
4
这里有记录:http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#event_dispatching 一旦GUI界面可见,大多数程序都由事件驱动,例如按钮操作或鼠标点击,这些事件始终在事件分派线程中处理。从这里我们可以推断它们在EDT中运行。 - OscarRyz

14

即使你能够这样做,为什么要这样做呢?通常情况下,当人们想要做这样的事情时,意味着他们没有很好地将 UI 和业务逻辑分离。通常他们想要调用 ActionListener 中发生的一些逻辑而不需要执行该操作。

public void actionPerformed(ActionEvent ae) {
    //SomeLogic
}

//...

public void someOtherPlace() {
    //I want to invoke SomeLogic from here though!
}

但实际上解决方案是将该逻辑从ActionListener中提取出来,并从ActionListener和第二个位置调用它:

public void someLogic() {
    //SomeLogic
}

public void actionPerformed(ActionEvent ae) {
    someLogic();
}

//...

public void someOtherPlace() {
    someLogic();
}

虽然这是一个很好的评论,但它并没有真正回答问题。 - OscarRyz
@OscarRyz:即使好的建议比直接“回答”更有助于OP,你也会对其进行负评吗?这并不是很有帮助。 - Mark Peters
1
嗯,是的,但我有一个很好的理由。你的回答并不真正是一个回答,而是一条评论(确实非常好),而在原始问题下面有一个评论区。当然这不是个人问题。 - OscarRyz
@Mark,好的你是可以这样做的(因为你绝对不是新手,所以我很惊讶你不知道)。这就是“社区维基”的用途。但是再次强调,我并没有针对你个人。所以,你能否修改一下你的答案,让我撤回我的投票?你会重新得到+2分,而我也会得到+1分。http://i.imgur.com/CAgjP.png - OscarRyz
“你为什么想要这样做?”嗯,我想这样做是因为我正在编写自动化测试,并且我也想要UI覆盖率...我做错了吗?编辑:哦,算了--我只需要doClick。好的,我明白了。 - senderle
显示剩余2条评论

3

只有当您继承并公开受保护的fireActionPerformed方法时,才能使用它:

class MockButton extends JButton { 
   // bunch of constructors here 
   @Override 
   public void fireActionPerformed( ActionEvent e ) { 
       super.fireActionPerformed( e );
   }
}

然后你就能够使用一个类似这样的参考文献,当然,你需要按照以下格式来使用它:
MockButton b = .... 

b.fireActionPerformed( new Action... etc. etc

你为什么想这样做呢?我不知道,但我建议你遵循Mark的建议


1
如果您不想在按钮上调用doClick(),则可以直接调用由按钮动作调用的代码。也许您希望拥有任何保存actionPerformed方法的类调用其他类可以调用的公共方法,并简单地调用此方法。

0
实际问题已经得到解决,似乎(参见Mark Peters'jjnguy的回答)。同时,也已经提到了fireActionPerformed方法(参见OscarRyz'的回答),以避免潜在的并发问题。
我想要补充的是,你可以使用反射调用所有私有和受保护的方法(包括fireActionPerformed),而无需子类化任何类。首先,你需要使用method = clazz.getDeclaredMethod()获取一个私有或受保护方法的反射Method对象(clazz需要是声明该方法的类的Class对象,而不是它的子类(例如,对于方法fireActionPerformed,需要使用AbstractButton.class,而不是JButton.class))。然后,你需要调用method.setAccessible(true)来抑制访问私有或受保护方法/字段时会出现的IllegalAccessException异常。最后,你需要调用method.invoke()
然而,我对反射了解不够,无法列举使用反射的缺点。根据Reflection API trail(参见“Reflection的缺点”一节),这些缺点确实存在。
以下是一些可行的代码示例:
// ButtonFireAction.java
import javax.swing.AbstractButton;
import javax.swing.JButton;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Method;

public class ButtonFireAction
{
    public static void main(String[] args) throws ReflectiveOperationException
    {
      JButton button = new JButton("action command");
      Class<AbstractButton> abstractClass = AbstractButton.class;
      Method fireMethod;

      // signature: public ActionEvent(Object source, int id, String command)
      ActionEvent myActionEvent = new ActionEvent(button,
                                                  ActionEvent.ACTION_PERFORMED,
                                                  button.getActionCommand());
      button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e)
        {
          System.out.println(e.getActionCommand());
        }
      });

      // get the Method object of protected method fireActionPerformed
      fireMethod = abstractClass.getDeclaredMethod("fireActionPerformed",
                                                   ActionEvent.class);
      // set accessible, so that no IllegalAccessException is thrown when
      // calling invoke()
      fireMethod.setAccessible(true);

      // signature: invoke(Object obj, Object... args)
      fireMethod.invoke(button,myActionEvent);
    }
}

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