15

How do I programmatically send an ActionEvent (eg button pressed/ACTION_PERFORMED) to a JButton?

I know of:

button.doClick(0);

and

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

But isn't it possible to directly send it an ActionEvent?

EDIT: This is not production code, it's just a little personal experiment.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Anon
  • 203
  • 1
  • 2
  • 6

5 Answers5

17

You can get a button's ActionListeners, and then call the actionPerformed method directly.

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);
}
Nathan
  • 8,093
  • 8
  • 50
  • 76
jjnguy
  • 136,852
  • 53
  • 295
  • 323
  • 4
    One thing to watch out for is that often ActionListeners assume they're going to be invoked on the EDT, so if you're going to dispatch them manually you'll want to do so on the EDT as well. – Mark Peters Jan 20 '11 at 21:59
  • Regarding my comment: does anybody know of somewhere where it's *documented* that you can assume your ActionListener will be run on the EDT? – Mark Peters Jan 20 '11 at 22:02
  • 4
    It is *documented* here: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#event_dispatching *Once the GUI is visible, most programs are driven by events such as button actions or mouse clicks, which are **always** handled in the event-dispatching thread* From there we can assume they run in the EDT. – OscarRyz Jan 20 '11 at 22:06
14

Even if you could, why would you want to? Usually when people want to do something like this, it means they haven't properly separated the concerns of the UI from business logic. Typically they want to invoke some logic that occurs in an ActionListener without needing the action to take place.

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

//...

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

But really the solution is to extract that logic from the ActionListener and invoke it from both the ActionListener and that second location:

public void someLogic() {
    //SomeLogic
}

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

//...

public void someOtherPlace() {
    someLogic();
}
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • While this is a good comment, it doesn't really answer the question. – OscarRyz Jan 20 '11 at 21:57
  • @OscarRyz: so you downvote good advice even if it helps the OP more than a straight-up "answer"? That's not very helpful. – Mark Peters Jan 20 '11 at 21:58
  • 1
    Well, yes, but I have a good reason for that. Your answer is not really an answer, is a comment ( very good indeed ) , and there is a place for comments right below the original question. This is not personal of course. – OscarRyz Jan 20 '11 at 22:02
  • @OscarRyz: Well it was intended to be an answer in that there's a good chance it solves the OP's *problem* (and if not the OP than almost certainly somebody coming later), if not their specific *question*. But I find you need to allow for exceptions even when it should be a comment. I couldn't have done this explanation in a comment due to technical limits. – Mark Peters Jan 20 '11 at 22:05
  • @Mark, well you **can** ( it surprise me you don't know that, because you're definitely not a newbie ) That's what the "community wiki"s are for. But again, I didn't mean to take this personal. So, could you "touch" your answer so I can take my downvote back? You'll get your +2 pts. back and I'll get my +1 pt. http://i.imgur.com/CAgjP.png – OscarRyz Jan 20 '11 at 22:12
  • Lol I hate that vote locking. Sure I'll touch it though I wasn't as worried about the d/v as much as genuinely interested in your POV. I've seen you around enough to know it's not personal. As for CW, I don't think that's what it's for at all. People abuse it by using it as a way to not get rep for an answer; it's made for collaborative answers/questions. You're just saying I shouldn't get rep for this answer which I would argue against 'cause I think this particular one is a legit answer :-). – Mark Peters Jan 20 '11 at 22:15
  • "Why would you want to?" Well, I want to because I'm writing an automated test, and I want UI coverage too... am I doing it wrong? Edit: Oh, NVM -- I just need `doClick`. OK, I get it. – senderle Mar 31 '16 at 15:27
3

Only if you inherit and expose the fireActionPerformed method, which is protected:

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

Then you will be able, but of course, you have to use a reference like this:

MockButton b = .... 

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

Why would you like to do that? I don't know, but I would suggest you to follow Mark's advice

Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
1

If you don't want to call doClick() on the button, then you can simply call the code called by the button's action. Perhaps you want to have whatever class that holds the actionPerformed method call a public method that other classes can call, and simply call this method.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
0

The practical problem was solved, it seems (see Mark Peters' and jjnguy's answers). And the fireActionPerformed method was also already mentioned (see OscarRyz' answer), for avoiding potential concurrency problems.

What I wanted to add was that you can call all private and protected methods (including fireActionPerformed), without the need to subclass any classes, using reflection. First, you get the reflection Method object of a private or protected method with method = clazz.getDeclaredMethod() (clazz needs to be the Class object of th class that declares the method, not one of its subclasses (i.e. AbstractButton.class for the method fireActionPerformed, not JButton.class)). Then, you call method.setAccessible(true) to suppress the IllegalAccessExceptions that will otherwise occur when trying to access private or protected methods/fields. Finally, you call method.invoke().

I do not know enough about reflection, however, to be able to list the drawbacks of using reflection. They exist, though, according to the Reflection API trail (see section "Drawbacks of Reflection").

Here some working code:

// 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);
    }
}
Community
  • 1
  • 1
mkdrive2
  • 268
  • 1
  • 14