如何实现和保存多个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()); }