重构多个actionListeners

时间:2009-12-08 01:44:35

标签: java actionlistener

我目前正在研究一些Java代码,其中定义了很多ActionListener(每个JButton一个),并且有大约60个按钮。这些都被定义为JButton.addActionListener方法中的匿名内部类。我一直在考虑重构这个方法,以使代码看起来更整洁,因为它使它看起来非常混乱。我想过可能将监听器带入一个单独的类,它基本上有一些静态方法,每个方法都返回一个监听器。这意味着代码看起来像addActionListener(GetActionListener.addActionListener())。虽然这会让它更整洁,但我觉得它并不是一个优雅的解决方案。我还想到了一个静态的最终地图,它将KV对与侦听器名称一起保存到侦听器本身。然而,这似乎仍然不是一个非常优雅的解决方案。我想知道是否有人有任何想法?我还应该说所有的actionListeners都非常不同。

5 个答案:

答案 0 :(得分:2)

我建议不要使用ActionListener直接添加操作。如果你这样做,它就变得不可重复使用了。而是将您的行动包裹在javax.swing.Action课程中。这样您就可以在任何地方重复使用该操作。例如,现在您可以使用相同的操作来表示复制操作的菜单快捷方式和工具栏中的复制按钮。 基本上,我们的想法不是直接将可运行的动作与GUI元素结合在一起。

现在回答你的问题。我会在一个名为ActionRepsoitory的类中使用公共方法public repository of actions创建一个Action getAction(String)。每个操作都将由String常量标识,您可以使用该常量从存储库中检索操作。通常,该字符串将是元素的actionCommand。如何通过HasMap或其他方式管理ActionRepository中的操作完全取决于您。

这就是它在大多数专业代码AFAIK中的表现。

答案 1 :(得分:0)

Not a duplication of this question(这不是它回答的问题的副本......哇)但答案应该适用。

如果你的内部类不只是在外部类中调用一个方法,那么你就是“错误”(对我的“权利”的定义)。在发布的代码中,对increment()和decrement()的调用是“正确”的方式。重构代码让侦听器将方法调用转发给外部类是一个开始使代码更易于管理的好地方。

有人说......用户界面上有60个按钮?!真!哎哟!它们都在一个屏幕上,还是用标签或其他东西完成的? (如果它是标签或我在答案中提供更多内容的东西)。

答案 2 :(得分:0)

您可以创建一个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时程序将失败(因为我们在创建动作侦听器时查找方法)。这在编译时并不像编译时那么好,但是当触发动作时仍然比完全后期更好。

答案 3 :(得分:0)

我会推荐类似你建议的东西 - 创建一个你订阅所有事件的监听器类。您可能希望为每个事件使用该类的不同实例,告诉实例(在构造函数中)通常如何处理此特定事件。

这样做的好处是,您可以开始将侦听器内部的代码分解为更少的方法,因为它通常非常相似。有时你可以把它变成一种方法。

我用于菜单创建的“Pure dispatch”情况的一个技巧是指定菜单,菜单结构以及每个菜单项链接到数据的方法。需要一点反思,但它有效。

事实上 - 让我看看。

是的,我将这些课程保存在谷歌文档中:)数据指定如下:

final static String[] menus = { "File:*", "Save:save", "Load:load", "(", "Print:-", "Preview:preview", ")", "Quit:quit" };

它只解析了这个。文件因为启动而成为顶级项目,保存将调用您的“保存”方法,加载将调用您的“加载”方法,打印是子菜单(因此是parens),在其下方预览并且打印未绑定任何事情。

此字符串可以通过一次调用创建并绑定整个菜单。

如果您想玩它,可以使用my source code

顶部的“TestMenu”类是一个测试类,演示如何使用buildMenus方法。

这是在几年前完成的,我现在可能会采用不同的方式,但它确实有效。我不确定我是否喜欢它实际生成菜单,我想我会让字符串解析器使用单个字符串而不是将其分解为每个项目的字符串 - 应该很容易确保每个项目都是空格分隔。 ..

更好的API可能是这样的绑定方法:

bind(this, new JButton("Save"), "save", this);

按下保存按钮会导致在此(或您传入的任何其他对象)上调用save方法。您甚至可以将“save”参数设置为可选,只需使用JButton.getText()。toLower()作为调用方法,如果不存在参数(我猜这是配置之前的约定)

我没有用菜单这样做,因为我还想将菜单创建和菜单关系抽象为我的数据。

请注意,以这种方式编码是一种在Java中实现MVC分离的绝佳方式 - 您的所有控制器代码都可以从您的视图中删除。

答案 4 :(得分:0)

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
}

}