Java 重构多个ActionListener
我目前正在编写一些Java代码,其中定义了很多ActionListener(每个Java 重构多个ActionListener,java,actionlistener,Java,Actionlistener,我目前正在编写一些Java代码,其中定义了很多ActionListener(每个JButton),大约有60个按钮。这些都被定义为JButton.addActionListener方法中的匿名内部类。我一直在考虑如何重构它,使代码看起来更整洁,因为这会使它看起来非常混乱。我考虑将侦听器放入一个单独的类中,该类本质上有一个静态方法负载,每个静态方法返回一个侦听器。这意味着代码看起来像addActionListener(GetActionListener.addActionListener())。虽
JButton
),大约有60个按钮。这些都被定义为JButton.addActionListener
方法中的匿名内部类。我一直在考虑如何重构它,使代码看起来更整洁,因为这会使它看起来非常混乱。我考虑将侦听器放入一个单独的类中,该类本质上有一个静态方法负载,每个静态方法返回一个侦听器。这意味着代码看起来像addActionListener(GetActionListener.addActionListener())
。虽然这会使它更整洁,但我觉得这并不是一个真正优雅的解决方案。我还想到了一个静态的最终映射,将带有侦听器名称的KV对映射到侦听器本身。然而,这似乎仍然不是一个非常优雅的解决方案。我想知道有没有人有什么想法?我还应该说,所有的ActionListener都非常不同。(这不是它回答的问题的重复…哇),但答案应该适用
如果您的内部类所做的不仅仅是在外部类内部调用方法,那么您所做的就是“错误的”(根据我对“正确”的定义)。在发布的代码中,对increment()和decrement()的调用是实现这一点的“正确”方法。重构代码,让侦听器将方法调用转发给外部类,这是使代码更易于管理的好起点
话虽如此。。。用户界面上有60个按钮?!真正地哎哟它们都在一个屏幕上,还是通过标签或其他方式完成?(如果是选项卡或我在回答中有更多的内容提供)。我建议不要使用
ActionListener
直接添加操作。如果这样做,它将变得不可重用。相反,将您的操作包装在javax.swing.Action
类中。这样,您就可以在任何需要的地方重用该操作。例如,现在您可以对复制操作的菜单快捷方式和工具栏中的复制按钮使用相同的操作。
基本上,这个想法不是直接将可运行操作与GUI元素耦合
现在来回答你的问题。我将在一个名为actionrepsotory的类中创建一个动作的存储库,该类使用公共方法publicAction-getAction(String)
。您的每个操作都将由一个字符串常量标识,您可以使用该常量从存储库中检索操作。通常,该字符串将是元素的actionCommand
。如何通过HasMap或其他方式管理ActionRepository中的操作完全取决于您
这就是它在大多数专业代码中的作用,AFAIK。您可以创建一个特殊的ActionListener子类,它使用反射来调用给定的方法名,然后您可以像普通方法一样实现所有60个操作
package com.example;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MethodAsActionListener implements ActionListener {
private Object receiver;
private Method method;
public MethodAsActionListener(Object receiver, String name) {
this.receiver = receiver;
try {
this.method = receiver.getClass().getDeclaredMethod(name);
} catch (SecurityException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@Override
public void actionPerformed(ActionEvent event) {
try {
method.invoke(receiver);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
然后,如果你有一个方法调用你的类
private call(String name) {
return new MethodAsActionListener(this, name);
}
然后您可以按如下方式添加您的操作
button1.addActionListener(call("methodName1"));
button2.addActionListener(call("methodName2"));
button3.addActionListener(call("methodName3"));
button4.addActionListener(call("methodName4"));
button5.addActionListener(call("methodName5"));
如果缺少其中一个方法,则在构建UI时程序将失败(因为我们在创建操作侦听器时查找该方法)。这并不像编译时那样好,但仍然比触发操作时完全延迟绑定要好。我建议使用类似于您建议的方法--创建一个订阅所有事件的侦听器类。不过,您可能希望为每个事件使用不同的类实例,告诉实例(在构造函数中)如何处理此特定事件
这样做的好处是,您可以开始将侦听器中的代码分解为较少的方法,因为它们通常非常相似。有时候你可以用一种方法来实现
我在菜单创建的“纯分派”情况下使用的一个技巧是指定菜单、菜单的结构以及每个菜单项链接到数据中的方法。需要一点思考,但它是有效的
事实上,让我看看
是的,我把这些类保存在google文档中:)数据是这样指定的:
final static String[] menus = { "File:*", "Save:save", "Load:load", "(", "Print:-", "Preview:preview", ")", "Quit:quit" };
bind(this, new JButton("Save"), "save", this);
它刚刚解析了这个。由于开始,文件成为顶级项,保存将调用您的“保存”方法,加载将调用您的“加载”方法,打印是一个子菜单(因此是选项),它下面有预览,打印不绑定任何内容
此字符串可以通过一次调用创建和绑定整个菜单
如果你想玩的话,给你
顶部的“TestMenu”类是演示如何使用buildMenus方法的测试类
这是几年前做的,我现在可能会做不同的事情,但它是有效的。我不确定我是否喜欢它实际生成菜单,我想我应该让字符串解析器使用单个字符串,而不是将其分解为每个项的字符串--应该很容易确保每个项都是空格分隔的
更好的API可能是这样的绑定方法:
final static String[] menus = { "File:*", "Save:save", "Load:load", "(", "Print:-", "Preview:preview", ")", "Quit:quit" };
bind(this, new JButton("Save"), "save", this);
其中,按下save按钮将导致对该对象(或您传入的任何其他对象)调用save方法。您甚至可以将“save”参数设置为可选,如果不存在参数,只需使用JButton.getText().toLower()作为调用方法(我想这是配置之前的惯例)
我没有用这种方式处理菜单,因为我还想将菜单创建和菜单关系抽象到我的数据中
注意,用这种方式编码是在Java中实现MVC分离的一种非常好的方法——所有控制器代码都可以从视图中删除。在许多简单的GUI中,使用动作框架是一种过火的方法。然而,在这里,当试图管理大约60个ActionListener时,这是重构过程中的一个好步骤,即使您没有菜单或快捷方式。@akf…我不得不同意这一点,但这又取决于某些参数:a)是在其他地方使用的操作b)可维护性。在决定之前,你必须考虑这两点和更多
package hEvil;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;
public class JDial extends JDialog {
private static final long serialVersionUID = -26565050431585019L;
private final JPanel contentPanel = new JPanel();
public static void main(String[] args) {
try {
JDial dialog = new JDial();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
dialog.setTitle("Heavy Evil");
dialog.setBackground(Color.WHITE);
} catch (final Exception e) {
e.printStackTrace();
}
}
public JDial() {
setBounds(0, 0, 1300, 800);
getContentPane().setLayout(new BorderLayout());
contentPanel.setLayout(new FlowLayout());
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
getContentPane().add(contentPanel, BorderLayout.CENTER);
JPanel windowPane = new JPanel();
windowPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(windowPane, BorderLayout.SOUTH);
{
JButton cancelButton = new JButton("Exit");
cancelButton.setActionCommand("Exit");
windowPane.add(cancelButton);
cancelButton.setBounds(0, 0, 1200, 700);
}
{
JPanel textPane = new JPanel();
textPane.setLayout(new FlowLayout(FlowLayout.LEFT));
getContentPane().add(textPane, BorderLayout.NORTH);
textPane.setVisible(true);
{
JTextArea textArea = new JTextArea("Username", 2, 15);
textPane.add(textArea);
textArea.setWrapStyleWord(true);
textArea.setEditable(true);
textArea.setFont(Font.getFont(Font.SANS_SERIF));
textArea.setVisible(true);
textArea.enableInputMethods(isEnabled());
textArea.computeVisibleRect(getBounds());
textArea.setBackground(Color.GRAY);
JTextArea textArea2 = new JTextArea("Password", 2, 15);
textPane.add(textArea2);
textArea2.setWrapStyleWord(true);
textArea2.setEditable(true);
textArea2.setFont(Font.getFont(Font.SANS_SERIF));
textArea2.setVisible(true);
textArea2.enableInputMethods(isEnabled());
textArea2.computeVisibleRect(getBounds());
textArea2.setBackground(Color.GRAY);
}
{
JButton registerButton = new JButton("Register");
textPane.add(registerButton);
}
{
JButton newButton = new JButton("Submit");
textPane.add(newButton);
newButton.setEnabled(true);
getRootPane().setDefaultButton(newButton);
newButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
JFrame newFrame = new JFrame("Welcome");
newFrame.setVisible(true);
newFrame.setBackground(Color.BLACK);
newFrame.setBounds(0, 0, 580, 200);
JPanel newPanel = new JPanel();
newFrame.add(newPanel);
dispose();
JButton nuButton = new JButton("Mario");
newPanel.add(nuButton);
JButton nuButton2 = new JButton("Kirby");
newPanel.add(nuButton2);
JButton nuButton3 = new JButton("Mew Two");
newPanel.add(nuButton3);
JButton nuButton4 = new JButton("Vegeta");
newPanel.add(nuButton4);
JButton nuButton5 = new JButton("Tidus");
newPanel.add(nuButton5);
JButton nuButton6 = new JButton("Link");
newPanel.add(nuButton6);
JButton nuButton7 = new JButton("Master Chief");
newPanel.add(nuButton7);
JButton nuButton8 = new JButton("Snake");
newPanel.add(nuButton8);
JButton nuButton9 = new JButton("Cash");
newPanel.add(nuButton9);
JButton nuButton10 = new JButton("Lara");
newPanel.add(nuButton10);
JButton nuButton11 = new JButton("Max");
newPanel.add(nuButton11);
JButton nuButton12 = new JButton("Spyro");
newPanel.add(nuButton12);
JButton nuButton13 = new JButton("Sephiroth");
newPanel.add(nuButton13);
JButton nuButton14 = new JButton("Scorpion");
newPanel.add(nuButton14);
}
});
}
}
}
}
//AND I WANT TO BE ABLE TO IMPLEMENT EACH BUTTON FROM ANOTHER CLASS
//FROM ACTIONEVENT WITH SOMETHINGS SIMILAR TO nuButtonX.actionImplemented...
//CALLING THE SAME ACTIONLISTENER IF I CAN AND THEN CREATING A CLASS FOR
//MODIFICATIONS AT EACH INSTANCE.
enter code here
package hEvil;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
public class ActoEve extends ActionEvent {
/**
*
*/
private static final long serialVersionUID = -2354901917888497068L;
public ActoEve(Object arg0, int arg1, String arg2) {
super(ActionEvent.ACTION_PERFORMED, arg1, "flame_001.jpg");
// TODO Auto-generated constructor stub
}
public void actionImplemented(ActionEvent evt1) {
JFrame nuFrame = new JFrame();
nuFrame.setVisible(true);
nuFrame.setBounds(0, 0, 300, 200);
// TODO Auto-generated constructor stub
}
}