Java Event Binding

2020-07-28 12:08发布

问题:

I'm new in windows application development using java. The question is this "How to i bind events to custom class methods?

As fas i have seen i can register listener classes to swing components to handle events. That is OK but i have to implement a class that implements e.g. the ActionListener interface to handle an event and then implement the actionPerformed method. That means ONE class FOR each event i have to handle?

Can't i have ONE class "listening" for events from all components and have each method in this class do the event handling?"

An example:

class MyEventListener { //pseudo code [no constructors, etc]

    public void handleSubmitFormBtn(Event e) {

    }

    //other methods go here handling events from other swing components
}

Note: I am not sure about the method signature but i hope that you get the point.

Conclusion: ONE method to handle events fired from swing components..is it possible? Is the creation of ONE class for each the event the only way? Can't i route event handling to specific methods for a single class?

回答1:

You have a few choices, each with their particular benefits/drawbacks.

anonymous inner classes

component.addActionListener(
    new ActionListener()
    {
        public void actionPerformed(final ActionEvent e)
        {
            outerClassesMethod();
        }
    });

inner class

class Foo
    implements ActionListener
{
    public void actionPerformed(final ActionEvent e)
    {
        outerClassMethod();
    }
}

outer class

public class Foo
    implements ActionListener
{
    private final OuterClass target;

    public Foo(final OuterClass t)
    {
        target = t;
    }

    public void actionPerformed(final ActionEvent e)
    {
        target.targetClassMethod();
    }
}

class implements listener

public class OuterClass
    implements ActionListener
{
    public void actionPerformed(final ActionEvent e)
    {
        method();
    }

    // somewhere else in the code
    {
         component.addActionListener(this);
    }
}

Each way has good and bad to it.

The anonymous inner class will not allow you to do what you are asking, it can only implement one listener.

The other three will all allow you to do what you want (just add , WindowListener to the implements list for exaple). You likely want the inner class or outer class implementing the listener way to do what you want. I suggest that because the listener is likely very highly coupled to your program, and you will need to do a large set of "if" statements to figure out which control was acted on to perform the actual action (you use evt.getSource() to figure out which control was being acted on and then comare it to your instance variables to see which it was).

However, unless you are in memory constrained device, such as an Android phone, you probably should not do one method for all listeners as it can easily lead to very bad code. If memory is an issue, then go for it, but if it isn't you are better of doing one of the following things:

  • one listener class per control
  • one listener class per event type for all controls
  • one listener class per control per event type

I prefer to code the following way, I find it to be the most flexible:

public class Outer
    extends JFrame
{
    private final JButton buttonA;
    private final JButton buttonB;

    {
        buttonA = new JButton("A");
        buttonB = new JButton("B");
    }

    // do not put these in the constructor unless the Outer class is final
    public void init()
    {
        buttonA.addActionListener(new AListener());
        buttonB.addActionListener(new BListener());
    }

    private void aMethod()
    {
    }

    private void bMethod()
    {
    }

    public void AListener
        implements ActionListener
    {
        public void actionPerformed(final ActionEvent evt)
        {
            aMethod();
        }
    }

    public void BListener
        implements ActionListener
    {
        public void actionPerformed(final ActionEvent evt)
        {
            bMethod();
        }
    }
}

I prefer this way because it forces the methods out of the listeners, which means I only have one place to look for the code (not scattered throughout the inner classes). It also means that it is possible that aMethod() and bMethod() can be reused - if the code is in a listener that isn't practical (it is possible, but not worth the effort).

Doing it the above way is also consistent, and I prefer consistency over most things unless there is a good reason not do it. For instance on the Android I do not do that since class creation is expensive (I still have the listener call methods only, but the class itself implements the listeners and I do an if statement).



回答2:

In swing what you usually do is use an anonymous class to handle your events, like so:

  someControl.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
          // handle the event
      }
  });

Although you can basically have one class handling all your events, and register it to all handlers, anonymous classes, as mentioned above, are the correct swing idiom for handling events.

You can find much more info @ http://java.sun.com/docs/books/tutorial/uiswing/events/generalrules.html



回答3:

You can do this by creating a single action listener that then switches based on the input source, so something like:

public void actionPerformed(ActionEvent e) {
   if (e.getSource() == buttonA) {
       doSomethingForButtonA(e);
   } else if (e.getSource() == buttonB) {
       doSomethingForButtonB(e);
   }
}

But this is not the recommended way to do it for various reasons. Why do you have an issue with creating a listener for every event to be received? It's the Java model for handling UI events, and if anyone else uses your code or you ever use someone else code it's going to be expected.



回答4:

You can have one class which has listener methods of many events:

class EventHandler implements ActionListener, ..., MouseListener {
   // implementation
}


回答5:

A conceptual Solution would be to implement the OBSERVER PATTERN.



回答6:

Melasse framework allows to glue UI component with model w/o creating class (even anonymous one), with syntax Binder.bind(/* source, target, options */).

For example not new class is needed to enable an action/button only when some text is present in a textfield: https://github.com/cchantep/melasse/blob/master/README.md#bind-buttonaction-to-provided-value . Same to display/hide informational or error label.

Most UI components are supported, all Java Beans (with property change support) are.