4

好的,我有一个包含多个 Menu 和 MenuItem 的类(我们称之为:MenuBarClass)。我想为每个 MenuItem 分配一个动作侦听器,但是.. 而不是做类似的事情:

menuitem_1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });
menuitem_2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });
menuitem_3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });
// ...
menuitem_N.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {} });

我希望我的代码更易于维护,而且……更重要……我不想在一个巨大的 ActionListener 类中出现很多“如果”,例如:

public void actionPerformed(ActionEvent e) {
  if (e.getSource().equals(menuitem_1)) {
    //do stuff..
  } else if (e.getSource().equals(menuitem_2)) {
    //do stuff..
  } else ...
}

如果可能的话,我该怎么做?任何人都可以帮忙吗?

4

4 回答 4

2

您可以使用反射API 创建实用方法来减少冗长:

package demo;    
import java.awt.event.*;
import java.lang.reflect.*;

public class ListenerProxies {    
  private static final Class<?>[] INTERFACES = { ActionListener.class };

  public static ActionListener actionListener(final Object target,
                                                    String method) {
    final Method proxied = method(target, method);
    InvocationHandler handler = new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable {
        ActionEvent event = (ActionEvent) args[0];
        return proxied.invoke(target, event);
      }
    };
    return (ActionListener) Proxy.newProxyInstance(target.getClass()
        .getClassLoader(), INTERFACES, handler);
  }

  private static Method method(Object target, String method) {
    try {
      return target.getClass().getMethod(method, ActionEvent.class);
    } catch (NoSuchMethodException e) {
      throw new IllegalStateException(e);
    } catch (SecurityException e) {
      throw new IllegalStateException(e);
    }
  }
}

这可以像这样使用:

package demo;
import static demo.ListenerProxies.actionListener;
import java.awt.event.ActionEvent;
import javax.swing.*;

public class Demo {

  public static void main(String[] args) {
    Demo test = new Demo();
    JButton hello = new JButton("Say Hello");
    hello.addActionListener(actionListener(test, "sayHello"));
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(hello);
    frame.pack();
    frame.setVisible(true);
  }

  public void sayHello(ActionEvent event) {
    System.out.println("Hello");
  }
}

这样做的缺点是缺少编译时检查该sayHello(ActionEvent)方法是否存在。

性能成本可以忽略不计。

于 2012-04-22T21:16:01.037 回答
1

实际上,这些ActionListener对象是通过命令设计模式的命令对象。您可以创建自定义子类而不是匿名子类,并获得更多的优雅。

现在,如果困扰您的是您如何将动作侦听器与命令对象连接起来,我会使用反射来做这样的事情:

  • 创建一个自定义注释,类似于@MenuAction它可能采用正确命令对象的类。
  • 创建一个通用的动作监听器来读取、实例化和执行这个命令。
  • 将通用动作侦听器添加到所有菜单项。

如果您认为它好,您可以创建一个框架并在多个项目中使用这种通用方法,但这比简单地ActionListener手动连接几个菜单项和正确的实现要多得多。

于 2012-04-22T21:02:48.330 回答
1

如果每个菜单项您想要做的事情都相似,您可以创建一个实现ActionListener构造函数参数的类。例如,如果每个菜单项都应该打开一个JFrame,您可以执行以下操作:

public class OpenFrameAction implements ActionListener
{
    private final JFrame frame;

    public OpenFrameAction(final JFrame frameToOpen)
    {
        this.frame = frameToOpen;
    }

    public void actionPerformed(ActionEvent e)
    {
        this.frame.setVisible(true);
    }
}

然后对于每个菜单项:

menuitem_1.addActionListener(new OpenFrameAction(myFrameForMenuItem1));
于 2012-04-22T21:06:19.270 回答
0

扩展siegi的答案。如果要执行的动作有共同点(跳下悬崖、跳探戈、喝杯咖啡),你真的只想为每个项目添加单独的听众。如果是这种情况,您不能指望 Java 为您执行任何可维护性魔法。

更常见的情况是动作确实有一些共同点(做探戈,做狐步舞等)。如果是这种情况,您可以按照 siegi 的建议或将侦听器附加到菜单(而不是项目)。该事件应该告诉您选择了哪个项目,您可以在侦听器中使用它:

// something like this
actionPerformed(ActionEvent e)
{
    this.doDance(e.getSource().getSelectedValue());
}
于 2012-04-22T21:27:17.243 回答