views:

160

answers:

8

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).

A: 

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.

BalusC
But value of the arguments of the actionPerformed should depend on which button is pressed.
Roman
You just specify that as argument on creation, do you?
BalusC
I do not understand. On creation of what? I specify what as argument? The value of the `action` variable is specific for every button. If I create a final variable "action" with a specific value it will be just one value. And I have many buttons. They will see the same value and they need to get different values.
Roman
A: 

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

Andreas_D
A: 

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));
Syntactic
Can I have a constructor in the ActionListener class?
Roman
You can have a constructor in any class you want, if it's a proper class (as opposed to an anonymous inner class, like you're using in your example).
Syntactic
+4  A: 

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) );
Eli Courtwright
+1  A: 

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.

justkt
This solutions looks attractive to me. There is just one problem. I cannot create the inner class. Can it be create on the same file where the "outer" class is? If it is the case, should be the inner class in the `{...}` of the "outer" class? If I do this way, compiler complains. It says that `;` should be put. If I put `;` after `{...}` of the inner class, it still complains.
Roman
@Roman - I have modified the class to show how I usually construct inner classes. I am unsure what you are talking about when you mention a semi-colon. Where were you putting a semi-colon?@Rahul G - from the original poster's actionPerformed method.
justkt
@justkt, thanks for the answer. I found out the problem. It was because of the conflict between different inner classes (I described it in the "ADDED" section of my original question). So, I decided to program another class (not the inner one).
Roman
A: 

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();
            }
        });
    }
}
MetroidFan2002
A: 

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.

Devon_C_Miller
+1  A: 

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.

Tom Hawtin - tackline