题
我目前正在处理一些在其中定义了很多actionListeners的Java代码(每个java代码 JButton
),大约有60个按钮。这些都被定义为在 JButton.addActionListener
方法。我一直在考虑重构的方法,以使代码看起来更整洁,因为这使它看起来非常混乱。我想到可能将听众带入一个单独的类,该课程本质上具有每个返回听众的静态方法。这将意味着代码看起来像 addActionListener(GetActionListener.addActionListener())
. 。尽管这将使它变得更整洁,但我觉得它并不是真正的优雅解决方案。我还认为静态的最终地图将KV对拿着带有听众名称的KV对给听众本身。但是,这似乎仍然不是一个非常优雅的解决方案。我想知道是否有人有任何想法?我还应该说所有的动作名单都大不相同。
解决方案
我建议不要直接添加使用 ActionListener
. 。如果您这样做,它将变得不可解决。而是将您的行为包裹在 javax.swing.Action
班级。这样您就可以在任何地方重复使用动作。例如,例如现在,您可以使用相同的操作来表示复制操作的菜单快捷方式和工具栏中的复制按钮。基本上,这个想法不是直接将可运行的动作与GUI元素搭配。
现在来到您的问题。我会做一个 repository of actions
在称为“行动式”的课程中,公共方法公共方法 Action getAction(String)
. 。您的每个操作都将通过一个字符串常数来识别,您用来从存储库中检索操作。通常,字符串将是 actionCommand
对于元素。您如何通过hasmap或其他任何方式管理ActionRopository中的操作,完全取决于您。
这就是它在大多数专业代码中的方式,Afaik。
其他提示
这不是这个问题的重复 (这不是它回答的问题的重复性...哇)但是答案应该适用。
如果您的内部类不仅在做外部类内部的方法,因此您正在做“错误”(对于我对“正确”的定义)。在已发布的代码中,调用rezement()和减少()是执行此操作的“正确”方法。重构代码让侦听器将方法调用到外类是开始使代码更易于管理的好地方。
话虽这么说... UI上的60个按钮?!真的!哎哟!它们都是在一个屏幕上还是使用标签或其他东西完成? (如果是标签或答案中我还有更多的东西)。
您可以做一个特殊的子类 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);
}
}
}
然后,如果您是一种方法 call
上课
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" };
它只是解析了这个。文件成为顶级项目,因为开始,保存将调用您的“保存”方法,加载将调用您的“加载”方法,打印是一个子菜单(因此是parens),在其下面预览,并且不限制打印对任何东西。
该字符串可以用一个调用创建和绑定整个菜单。
这是 我的源代码 如果您想玩它。
顶部的“ TestMenu”类是一个测试类,演示了如何使用BuildMenus方法。
这是几年前完成的,我现在可能会有所不同,但是它起作用了。我不确定我是否喜欢它实际生成菜单,并且我想我会让字符串解析器使用单个字符串,而不是将其分解为每个项目的字符串 - 可以轻松确保每个项目都分开。 ..
更好的API可能是这样的绑定方法:
bind(this, new JButton("Save"), "save", this);
按下保存按钮的地方将导致保存方法被调用此(或您传递给的任何其他对象)。您甚至可以使“保存”参数可选,只需使用jbutton.getText()。tolower()作为如果不存在参数(我猜这是在配置之前的约定),则可以调用的方法)
我没有使用菜单这样做,因为我也想将菜单创建和菜单关系抽象成我的数据。
请注意,以这种方式进行编码是在Java中进行MVC分离的一种了不起的方法 - 可以从您的视图中删除所有控制器代码。
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
}
}