如何实现和保存多个actionListener

好的,我有一个包含多个Menu和MenuItem的类(让我们称之为:MenuBarClass)。 我想给每个MenuItem分配一个actionlistener,但是……而不是做类似的事情:

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类中出现很多“if”,如:

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

我怎么能这样做,如果可能的话? 有人可以帮忙吗?

您可以使用reflection 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)方法的存在。

性能成本可以忽略不计。

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

现在,如果困扰你的是你如何使用命令对象连接动作监听器,我会使用reflection做这样的事情:

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

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

如果你想要做的事情对于每个菜单项是相似的,你可以创建一个实现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)); 

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

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

 // something like this actionPerformed(ActionEvent e) { this.doDance(e.getSource().getSelectedValue()); }