简体   繁体   中英

Java ActionListeners

I am going to be developing a game in Java, and it will have many listeners( action, key, mouse, etc..).

My Question is what is the preferable way to implement the listeners.

Method 1:

this.addActionListener(new ActionListener() {
   // Overide methods go here
});

Method 2:

create a new class(or multiple classes) which will implement ActionListener and have the methods for the different Game Componenets (buttons, movement, whatever else needs an ActionListener)

So, for instance. If I am making a button is it better to do

JButton button = new JButton();
button.addActionListener(new ActionListener() {

});

or

JButton button = new JButton();
button.addActionListener(new MyActionListener());

// MyActionListener
class MyActionListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        Object objectPressed = e.getSource();
        if(objectPressed.equals(button) {
          System.out.println("Hello World");
        }
    }
}

I can see advantages in both ways, Method 1 you can see what is happening to that object directly, but method 2 you can see all components.

So when developing large scale applications, which is easier to maintain, having all listeners in separate classes, or using Method 1?

Personally, I would prefer many Listeners to one with "if" checks. It would allow me to modify them independently.

I would embed that code inside the UI. I'd write them as separate classes and inject them as dependencies using constructor or DI factory.

Constructor injection:

public class YourPanel extends JPanel {
    private JButton button;

    public YourPanel(ActionListener buttonListener) {
        this.button = new JButton("Do It");
        this.button.addActionListener(buttonListener);
    }
}

First, terms. In both cases you are defining classes, but in the first one they are called anonymous inner classes . You will find some file like MyClass$1.java .

In that sense, MY (other may differ) rules would be

1) Use anonymous inner classes only for simpler actions that are not reused. For all the other, use "regular" classes.

2) Reuse components only when they make sense. If you have two buttons that have wildly different meanings, don't try to reuse the same listener. Create several ones. By example, if you have two buttons (an increment and a decrement) you can reuse the same class since operation will be very similar. If you have several values with such buttons, reuse the classes passing the object to be modified in the constructor. But do not mix a button to increment a value with a "start round" button.


UPDATE:

And, by the way, when you say:

I can see advantages in both ways, Method 1 you can see what is happening to that object directly, but method 2 you can see all components.

It may look like an advantage to see all components but makes encapsulating the logic way more difficult, if all of your classes can change all of the other. Encapsulation means some extra work but in the end you get a product that's more maintanable, and that is an important thing.

Just to add one more stick to this fire, myself, I prefer to use AbstractActions, either as an anonymous inner class, or more often as an independent stand-alone class:

JButton myExitButton = new JButton(new MyExitAction());

As an example, a Control class that is part of an MVC Swing project of mine has this as part of its code:

public class Control {

   // these two types below are interfaces
   private Model model;
   private View view;

   public Control(Model model, View view) {
      this.model = model;
      this.view = view;

      addViewListeners();
   }

   private void addViewListeners() {
      view.setGetPageAction(new GetPageAction(this, "Get Page", KeyEvent.VK_G));
      view.setSetTnRendererBtnAction(new SetTnRendererBtnAction(this, "Show Images", KeyEvent.VK_S));
      view.setClearThumbNailsAction(new ClearThumbNailsAction(this, "Clear ThumbNails", KeyEvent.VK_C));
      view.setSetDestinationAction(new SetDestinationAction(this, "Set Destination", KeyEvent.VK_T));
      view.setDownloadAction(new DownloadAction(this, "Download", KeyEvent.VK_D));
      view.setExitAction(new ExitAction(this, "Exit", KeyEvent.VK_X));
      model.addPropertyChangeListener(new ModelListener());
   }

   public View getView() {
      return view;
   }

   public Model getModel() {
      return model;
   }

   // .....
}

And the abstract class that underlies all of my AbstractActions looks like so:

public abstract class MyAbstractAction extends AbstractAction {

   protected Control control;
   protected Model model;
   protected View view;

   public MyAbstractAction(Control control, String txt, int mnemonic) {
      super(txt);
      putValue(MNEMONIC_KEY, mnemonic);
      this.control = control;
      this.model = control.getModel();
      this.view = control.getView();
   }

}

One caveat: Note that I'm not a professional programmer but rather a hobbiest, and so while my ideas work for me, they may not represent the absolute best in the field. All corrections and advice are most welcome. A weakness to my design above is that I think that I'm "injecting" my Actions in a clumsy way.

I would honestly prefer the first one. you're right the first approach you can easily see what's going on that Component. However. there will be times where a button will probably have the same behavior or same ActionListener, that being said I would prefer the approach 2. so you can easily reuse actionListeners.

You could also take a look at this swing framework called swingobjects that i have been working on.

https://github.com/Sethuraman/swingobjects

If you create a Jframe using the FrameFactory class, swingobjects will register a GlobalListener for you for all the widgets you have declared in your frame. What this allows you to do is, mark a method in your frame with an annotation like this:

@Action("<action name or button text>")
public void performAction(ActionEvent evt){
....
}

The GlobalListener will call this method via reflection. This means, you will not have to write if blocks, create anonymous inner classes. Everything is transparently handled for you. Take a look at the framework ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM