Question

I have a big table containing a button in each cell. These buttons are very similar and do almost the same. If I add an action listener to every button in this way:

tmp.addActionListener(new ActionListener(){
   @Override
   public void actionPerformed(ActionEvent evt) {
      proposition = proposition + action;
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            updatePropositionPanel();
         }
      });
   }
});

Actually, every action listener differ from all others by the value of the action. proposition and updatePropositionPanel are a field and a method of the class.

  1. First i thought that I can make it shorter if I do not use inner classes. So, I decided to program a new ActionListener class. But than I realized that in this case "proposition" will not be visible to the instances of this class.

  2. Then I decided to add the actionPerformed method to the current class and do that: addActionListener(this). But than I realized that I do not know how give arguments to the actionPerformed method.

So, how does it work. Can I add an action listener in a short and elegent way?

ADDED:

I liked the idea to program an inner class with a constructor which can take some arguments and actioPerformed method, which can use arguments given in the constructor. I started to do so and then realized that it creates a conflicts with other inner anonymous classes (used like in the above given code). So, I think I will create another class (not inner one).

Was it helpful?

Solution

You can create your own class and pass the data into the constructor. For example

public class MyActionListener
{
    private int proposition;
    private MyOtherClass moc;

    public MyActionListener(int proposition, MyOtherClass moc) {
        this.proposition = proposition;
        this.moc = moc;
    }

    public void actionPerformed(ActionEvent evt) {
        proposition += moc.action;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                moc.updatePropositionPanel();
            }
        });
    }
}

Then you can add it as normal, passing whatever arguments you like to the constructor:

tmp.addActionListener( new MyActionListener(proposition, this) );

OTHER TIPS

EDIT: I have altered the class to show construction with MyOuterClass.

Here is some code sketched out. Hopefully this gets at your 1, which is how I would implement it.

public class MyOuterClass {
    // member variables for MyOuterClass

    public MyOuterClass() {
        // ...constructor stuff here
    }
    // ...outer class stuff here - methods, etc.

    // The code each place you want to add the listener, somewhere in MyOuterClass
    tmp.addActionListener(new MyActionListener(poposition, action));


    // below outer class stuff, although it can really be most places, I usually put
    // it here - personal style preference.  Swing's classes often put inner
    // classes first

    /**
     * An inner class.
     */
    private MyActionListener implements ActionListener {
        /**
      * Object used here as a filler, replace with appropriate
      * class types
      */
        private Object proposition;
        private Object action;

        private MyActionListener(Object proposition, Object action) {
            this.proposition = proposition;
            this.action = action;
        }
        public void actionPerformed(ActionEvent evt) {
          proposition = proposition + action;
          SwingUtilities.invokeLater(new Runnable() {
             public void run() {
                updatePropositionPanel();
             }
        }
        /**
         * Setters provided in case you need to change proposition and action.  If not,
         * feel free not to have them and to have final members
         */
        private void setProposition(Object proposition) {
            this.proposition = proposition;
        }
        private void setAction(Object action) {
            this.action = action;
        }
    }
}

EDIT: To create another class as you asked for in your edit, do as above but create a non-private other class in another .java file and code away.

As the only different is in the value of action you can place the code within a method. (Also the @Override is unnecessary, and += is useful here.)

public void setup(
    final AbstractButton button,
    final int action
) { 
    button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            proposition += action;
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                   updatePropositionPanel();
                }
           });
        }
    });
}

The invokeLater is probably pointless as you will be on the AWT Event Dispatch Thread (EDT) anyway.

If you are adding lots of general purpose actions, then you could simplify it by using an interface that doesn't have a pointless event object associated with it.

If you wanted to be hackish you could add the listener in a subclass constructor.

    new ActionHandler(button) { public void action() {
        proposition += action;
        updatePropositionPanel();
    }});

Hopefully, JDK7 will make the Java syntax for this sort of thing less verbose. Java will, however, always be somewhat verbose.

Option 1 works if you make proposition mutable (e.g. StringBuilder instead of String). Option 2 works if you declare them final. This way they're accessible/visible in the inner class.

You could create a separate MyActionListener class and pass the two values proposition and action with the constructor. It declutters the source code.

You could create your own listener class that implements ActionListener. This class could contain member variables corresponding to the parameters you're talking about; you'd set them using the constructor.

The call to add the listener would then look something like this:

tmp.addActionListener(new MyActionListenerSubClass(proposition, action));

I'll assume that your proposition and action variables are Strings for the sake of this example. Define an interface PropositionUpdater, an interface PropositionPanelUpdater, and a collaborator proposition holder:

public interface PropositionUpdater() {
    public void updateProposition(PropositionHolder holder, String action);
}

public interface PropositionHolder() {
    public String getProposition();

    public void setProposition(String proposition);
}

public interface PropositionPanelUpdater() {
    public void updatePropositionPanel();
}

The default implementation of a proposition updater is simply this:

public class DefaultPropositionUpdater implements PropositionUpdater {
    public void updateProposition(final PropositionHolder holder, final String action) {
        holder.setProposition(holder.getProposition() + action);
    }
}

I'll leave the default of a PropositionHolder and PropositionPanelUpdater to your imagination ;)

Now, here's your action listener:

public class PropositionUpdaterActionListener implements ActionListener {
    private PropositionHolder holder;

    private PropositionUpdater updater;

    private PropositionPanelUpdater panelUpdater;

    public PropositionUpdaterActionListener(final PropositionHolder holder, final PropositionUpdater updater, final PropositionPanelUpdater panelUpdater) {
        super();
        this.holder = holder;
        this.updater = updater;
        this.panelUpdater = panelUpdater;
    }

    public void actionPerformed(final ActionEvent evt) {
        //Not sure how you *got* the action, but whatever...
        updater.updateProposition(holder, action);
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                panelUpdater.updatePropositionPanel();
            }
        });
    }
}

You didn't say how many buttons you are talking about.

If it's a small number like a chess board or less, @juskt's approach is a good one.

However, if you looking at something larger, I would use this approach:

public class MyActionListener {
      public void actionPerformed(ActionEvent evt) {
          JComponent c = (JComponent)evt.getSoource();
          int prop = (Integer)c.getclientProperty("PROPOSITION");
          int act = (Integer)c.getclientProperty("ACTION");
          SomeClass obj = c.getclientProperty("UPDATE");
          prop += act;
          // If necessary, clientPut("PROPOSITION", prop);
          SwingUtilities.invokeLater(new    Runnable() {
              public void run() {
                  obj.updatePropositionPanel();
              }
          });
      }
}

This action listener holds no state. As a result, a single instance of it can be used for all of the buttons. For something like a Go board (19x19), this works out to 1 object instead of 361.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top