【问题标题】:Java ActionListenersJava 动作监听器
【发布时间】:2013-06-01 23:45:12
【问题描述】:

我打算用 Java 开发一个游戏,它会有很多监听器(动作、按键、鼠标等)。

我的问题是实现监听器的最佳方式是什么。

方法一:

this.addActionListener(new ActionListener() {
   // Overide methods go here
});

方法二:

创建一个新类(或多个类),它将实现 ActionListener 并具有用于不同游戏组件(按钮、移动、任何其他需要 ActionListener 的东西)的方法

所以,例如。如果我正在制作一个按钮,这样做会更好

JButton button = new JButton();
button.addActionListener(new ActionListener() {

});

JButton button = new JButton();
button.addActionListener(new MyActionListener());

// MyActionListener
class MyActionListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        Object objectPressed = e.getSource();
        if(objectPressed.equals(button) {
          System.out.println("Hello World");
        }
    }
}

我可以看到两种方式的优势,方法 1 你可以直接看到该对象发生了什么,但方法 2 你可以看到所有组件。

那么在开发更易于维护的大型应用程序时,将所有侦听器放在单独的类中,还是使用方法 1?

【问题讨论】:

    标签: java swing jbutton actionlistener anonymous-inner-class


    【解决方案1】:

    就我个人而言,我更喜欢多个 Listener,而不是一个带有“if”检查的 Listener。这将允许我独立修改它们。

    我会将该代码嵌入到 UI 中。我会将它们编写为单独的类,并使用构造函数或 DI 工厂将它们作为依赖项注入。

    构造函数注入:

    public class YourPanel extends JPanel {
        private JButton button;
    
        public YourPanel(ActionListener buttonListener) {
            this.button = new JButton("Do It");
            this.button.addActionListener(buttonListener);
        }
    }
    

    【讨论】:

    • 感谢您的更新!请看看我的回答。我做了类似的事情,但我觉得必须有一些更好的方法来做到这一点,使用某种间接方式,也许是配置文件,以防止我不得不对这种注入进行硬编码。再次感谢。
    • 我认为更好的方法是使用像 Spring 或 Guice 这样的 DI 引擎。
    • 谢谢,会调查的。我一直认为 Spring 主要用于企业服务器的东西,但它肯定比我意识到的更多。
    【解决方案2】:

    为了火上浇油,我自己更喜欢使用 AbstractActions,或者作为匿名内部类,或者更多时候作为独立的独立类:

    JButton myExitButton = new JButton(new MyExitAction());
    

    例如,作为我的 MVC Swing 项目一部分的 Control 类将其作为其代码的一部分:

    public class Control {
    
       // these two types below are interfaces
       private Model model;
       private View view;
    
       public Control(Model model, View view) {
          this.model = model;
          this.view = view;
    
          addViewListeners();
       }
    
       private void addViewListeners() {
          view.setGetPageAction(new GetPageAction(this, "Get Page", KeyEvent.VK_G));
          view.setSetTnRendererBtnAction(new SetTnRendererBtnAction(this, "Show Images", KeyEvent.VK_S));
          view.setClearThumbNailsAction(new ClearThumbNailsAction(this, "Clear ThumbNails", KeyEvent.VK_C));
          view.setSetDestinationAction(new SetDestinationAction(this, "Set Destination", KeyEvent.VK_T));
          view.setDownloadAction(new DownloadAction(this, "Download", KeyEvent.VK_D));
          view.setExitAction(new ExitAction(this, "Exit", KeyEvent.VK_X));
          model.addPropertyChangeListener(new ModelListener());
       }
    
       public View getView() {
          return view;
       }
    
       public Model getModel() {
          return model;
       }
    
       // .....
    }
    

    作为我所有 AbstractActions 基础的抽象类如下所示:

    public abstract class MyAbstractAction extends AbstractAction {
    
       protected Control control;
       protected Model model;
       protected View view;
    
       public MyAbstractAction(Control control, String txt, int mnemonic) {
          super(txt);
          putValue(MNEMONIC_KEY, mnemonic);
          this.control = control;
          this.model = control.getModel();
          this.view = control.getView();
       }
    
    }
    

    一个警告:请注意,我不是专业的程序员,而是最喜欢的人,因此虽然我的想法对我有用,但它们可能并不代表该领域的绝对最佳。欢迎所有更正和建议。我上面的设计的一个弱点是我认为我正在以一种笨拙的方式“注入”我的动作。

    【讨论】:

    • "请注意,我不是专业程序员,而是最业余的"这让我感到惊讶,因为我一直很喜欢你的回答。
    • @duffymo:谢谢。我认为我需要阅读注释,因为这可能会帮助我解决注入问题。
    • 我唯一不喜欢AbstractActions 的是@SuppressWarnings("serial") 在每一个或整个方法之前都需要(这是一个坏主意)。当然,这只有在他们是匿名的情况下才会发生。
    【解决方案3】:

    首先,条款。在这两种情况下,您都在定义类,但在第一种情况下,它们被称为匿名内部类。你会发现一些像MyClass$1.java这样的文件。

    从这个意义上说,我的(其他可能不同)规则将是

    1) 仅将匿名内部类用于未重用的简单操作。对于所有其他,使用“常规”类。

    2) 仅在有意义的情况下重用组件。如果您有两个含义截然不同的按钮,请不要尝试重用同一个侦听器。创建几个。例如,如果您有两个按钮(一个增量和一个减量),您可以重用同一个类,因为操作非常相似。如果您有多个带有此类按钮的值,请重用传递要在构造函数中修改的对象的类。但不要将增加值的按钮与“开始回合”按钮混用。


    更新:

    顺便说一句,当你说:

    我可以看到两种方式的优势,方法 1 你可以直接看到该对象发生了什么,但方法 2 你可以看到所有组件。

    查看所有组件可能看起来很有优势,但如果您的所有类都可以更改所有其他类,那么封装逻辑方式会更加困难。 封装意味着一些额外的工作,但最终你会得到一个更易于维护的产品,这很重要。

    【讨论】:

    • 对我来说,规则 #1 略有不同:它始终使用匿名内部类,但如果它是更大的代码块,则委托给封闭类中的方法。所以一个监听器可能只运行cardLayout.show(OuterClass.this, "HOME_VIEW"),但会委托给performSave()
    【解决方案4】:

    老实说,我更喜欢第一个。你是对的第一种方法,你可以很容易地看到那个组件发生了什么。然而。有时按钮可能具有相同的行为或相同的 ActionListener,也就是说我更喜欢方法 2。这样您就可以轻松地重用 actionListener。

    【讨论】:

      【解决方案5】:

      您还可以看看我一直在研究的这个名为 swingobjects 的摇摆框架。

      https://github.com/Sethuraman/swingobjects

      如果您使用 FrameFactory 类创建 Jframe,swingobjects 将为您在框架中声明的所有小部件注册一个 GlobalListener。这允许你做的是,在你的框架中用这样的注释标记一个方法:

      @Action("<action name or button text>")
      public void performAction(ActionEvent evt){
      ....
      }
      

      GlobalListener 将通过反射调用此方法。这意味着,您不必编写 if 块,创建匿名内部类。一切都为您透明地处理。看看框架...

      【讨论】:

        猜你喜欢
        • 2021-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-24
        相关资源
        最近更新 更多