【问题标题】:How to add action listener that listens to multiple buttons如何添加侦听多个按钮的动作侦听器
【发布时间】:2011-08-21 15:08:59
【问题描述】:

我正试图找出我对动作侦听器做错了什么。我正在关注多个教程,但当我尝试使用动作侦听器时,netbeans 和 eclipse 给了我错误。

下面是一个简单的程序,我试图让一个按钮在其中工作。

我做错了什么?

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;


public class calc extends JFrame implements ActionListener {



    public static void main(String[] args) {

        JFrame calcFrame = new JFrame();

        calcFrame.setSize(100, 100);
        calcFrame.setVisible(true);

        JButton button1 = new JButton("1");
        button1.addActionListener(this);

        calcFrame.add(button1);
    }

    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == button1)
    }  

}

动作监听器从未注册,因为if(e.getSource() == button1) 它看不到button1,错误提示找不到符号。

【问题讨论】:

  • 1) 类名应该是 EachWordUpperCase。 2)除非添加功能,否则不要扩展框架。 3) 如果扩展框架,您可能不需要在 main(String[]) 中实例化一个 4) 通常认为更好的做法是为每个需要的 GUI 元素添加一个动作侦听器,而不是使用一个 actionPerformed(ActionEvent) 方法一个巨大的 if/else 级联。 5) 如果您按照 (4) 中的建议进行操作,问题就会基本消失。
  • 可以使用操作 - 许多 UI 元素以一致的方式执行相同操作(例如工具栏和按钮)打开的最佳选择。见docs.oracle.com/javase/tutorial/uiswing/misc/action.html

标签: java swing jbutton actionlistener


【解决方案1】:

静态方法中没有this 指针。 (我不相信这段代码甚至会编译。)

你不应该在像main() 这样的静态方法中做这些事情;在构造函数中进行设置。我没有编译或运行它来查看它是否真的有效,但试一试。

public class Calc extends JFrame implements ActionListener {

    private Button button1;

    public Calc()
    {
        super();
        this.setSize(100, 100);
        this.setVisible(true);

        this.button1 = new JButton("1");
        this.button1.addActionListener(this);
        this.add(button1);
    }


    public static void main(String[] args) {

        Calc calc = new Calc();
        calc.setVisible(true);
    }

    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == button1)
    }  

}

【讨论】:

  • 完美,为我解决了问题,感谢您的帮助
【解决方案2】:

我很惊讶没有人提到使用动作命令。这是关联来源和听众的一种非常标准的方式。它真的很有用,如果;

  • 您有多个事件源需要执行相同的操作(例如,如果您希望用户能够按文本字段上的 Enter 键而不是单击旁边的按钮)
  • 您没有生成事件的组件的引用

看;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;    
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

public class DontExtendJFrame implements ActionListener {

  private enum Actions {
    HELLO,
    GOODBYE
  }

  public static void main(String[] args) {

    DontExtendJFrame instance = new DontExtendJFrame();

    JFrame frame = new JFrame("Test");
    frame.setLayout(new FlowLayout());
    frame.setSize(200, 100);

    JButton hello = new JButton("Hello");
    hello.setActionCommand(Actions.HELLO.name());
    hello.addActionListener(instance);
    frame.add(hello);

    JButton goodbye = new JButton("Goodbye");
    goodbye.setActionCommand(Actions.GOODBYE.name());
    goodbye.addActionListener(instance);
    frame.add(goodbye);

    frame.setVisible(true);
  }

  @Override
  public void actionPerformed(ActionEvent evt) {
    if (evt.getActionCommand() == Actions.HELLO.name()) {
      JOptionPane.showMessageDialog(null, "Hello");
    } else if (evt.getActionCommand() == Actions.GOODBYE.name()) {
      JOptionPane.showMessageDialog(null, "Goodbye");
    }
  }
}

【讨论】:

【解决方案3】:

这是根据我的评论修改的源代码形式。请注意,应该在 EDT 上构建和更新 GUI,尽管我没有走那么远。

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JFrame;

public class Calc {

    public static void main(String[] args) {

        JFrame calcFrame = new JFrame();

        // usually a good idea.
        calcFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        final JButton button1 = new JButton("1");
        button1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                JOptionPane.showMessageDialog(
                    button1, "..is the loneliest number");
            }
        });

        calcFrame.add(button1);

        // don't do this..
        // calcFrame.setSize(100, 100);

        // important!
        calcFrame.pack();

        calcFrame.setVisible(true);
    }
}

【讨论】:

  • 我的天,谢谢,我开始认为所有的 Java 代码都是垃圾,结果是开发人员很垃圾。
  • 这个 9 年的问题仍然有帮助
  • 很高兴它确实有帮助,@SHikhaMittal! :)
  • 顺便说一句,我是新手,我 4 个月前开始学习 java 需要帮助,这个堆栈溢出对初学者很不友好,虽然我一直在以错误的方式学习它,从现在开始,你能推荐任何可以建立我的基础的资源吗
  • @SHikhaMittal 我向语言本身的提供者推荐The Java Tutorials。这就是我学习 Java 的方式(好吧,这里还有 1000 多个问题和答案)。
【解决方案4】:

问题在于 button1 是一个局部变量。您只需更改添加 actionListener 的方式即可。

button.addActionListener(new ActionListener() {  
            public void actionPerformed(ActionEvent e)
            {
                //button is pressed
                System.out.println("You clicked the button");
            }});

或者您将button1 设为全局变量。

【讨论】:

  • 非常感谢您的帮助,这回答了我很多问题。
【解决方案5】:

您已被告知如何解决当前的问题,但我认为这里还有更重要的问题。

  • 遵守约定。即使是一次性代码。这意味着类名的初始情况。

  • 不要扩展你不需要的类。 JFrame 应该很少扩展。实际上,您并没有创建派生类的实例!!!

  • 不要将一堆东西捆绑到一个类中。特别是,您通常一次最多只能子类型化一个主类或接口(不包括 Comparable 之类的东西)。

  • 始终在 AWT 事件调度线程 (EDT) 上进行交互,包括构造、Swing/AWT GUI。它既丑陋又冗长,但这是适合您的 Java。

  • 检查事件的来源有点小技巧。听众很小,所以你甚至不能声称蹩脚的表演借口。

所以:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;


public class Calc {
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
            runEDT();
        }});
    }
    private static void runEDT() {
        assert java.awt.EventQueue.isDispatchThread();

        JFrame frame = new JFrame();

        frame.setSize(100, 100);

        JButton button1 = new JButton("1");
        button1.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                ...
            }
        });

        frame.add(button1);

        frame.setVisible(true);
    }
}

如果您需要从侦听器中的封闭方法访问任何变量,请将它们设为final

【讨论】:

    【解决方案6】:

    这里有很好的答案,但让我来谈谈添加监听多个按钮的动作监听器这一更全局的观点。

    有两种流行的方法。

    使用通用动作监听器

    您可以在 actionPerformed(ActionEvent e) 实现中获取操作的来源:

    JButton button1, button2; //your button
    
    @Override
    public void actionPerformed(ActionEvent e) {
    
        JButton actionSource = (JButton) e.getSource();
    
        if(actionSource.equals(button1)){
            // YOU BUTTON 1 CODE HERE
        } else if (actionSource.equals(button2)) {
            // YOU BUTTON 2 CODE HERE
        }
    }
    

    使用 ActionCommand

    通过这种方法,您可以设置按钮的actionCommand 字段,稍后您可以使用switch

    button1.setActionCommand("actionName1");
    button2.setActionCommand("actionName2");
    

    后来:

    @Override
    public void actionPerformed(ActionEvent e) {
        String actionCommand = ((JButton) e.getSource()).getActionCommand();
    
        switch (actionCommand) {
            case "actionName1": 
                // YOU BUTTON 1 CODE HERE
            break;
            case "actionName2": 
                // YOU BUTTON 2 CODE HERE
            break;
        }
    }
    

    查看learn more about JFrame Buttons, Listeners and Fields

    【讨论】:

      【解决方案7】:

      第一个问题是button1main方法的局部变量,所以actionPerformed方法无权访问它。

      第二个问题是ActionListener接口是由calc类实现的,但是main方法中没有创建这个类的实例。

      通常的做法是创建calc 的实例并将button1 设为calc 类的字段。

      【讨论】:

      • 非常感谢您的帮助,这回答了我很多问题。
      【解决方案8】:

      您在 main 方法中声明了 button1,因此您无法在 actionPerform 中访问它。您应该在课堂上将其设为全局。

       JButton button1;
       public static void main(String[] args) {
      
          JFrame calcFrame = new JFrame();
      
          calcFrame.setSize(100, 100);
          calcFrame.setVisible(true);
      
          button1 = new JButton("1");
          button1.addActionListener(this);
      
          calcFrame.add(button1);
      }
      
      public void actionPerformed(ActionEvent e) {
          if(e.getSource() == button1)
      }
      

      【讨论】:

      • 非常感谢您的帮助,这回答了我很多问题。
      【解决方案9】:

      首先,使用 super() 和构造函数正确扩展 JFrame 然后将动作侦听器添加到框架并添加按钮。

      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      import javax.swing.JButton;
      import javax.swing.JFrame;
      
      
      public class Calc extends JFrame implements ActionListener {
          JButton button1 = new JButton("1");
          JButton button2 = new JButton("2");
      
          public Calc()
          {
               setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
               setSize(100, 100);
               button1.addActionListener(this);
               button2.addActionListener(this);
               calcFrame.add(button1);
               calcFrame.add(button2);
          }
          public void actionPerformed(ActionEvent e)
          {
              Object source = e.getSource();
              if(source == button1)
              {
                  \\button1 code here
              } else if(source == button2)
              {
                  \\button2 code here
              }
          } 
          public static void main(String[] args)
          {
      
              JFrame calcFrame = new JFrame();
              calcFrame.setVisible(true);
          }
      }
      

      【讨论】:

      • 否(除了您根本没有使用 Calc 的疏忽——如果您实际运行您的示例,您会注意到什么;-) a) 不要扩展 JFrame,而是使用它 b) 不要实现 xxListener,而是使用它
      【解决方案10】:

      我使用“e.getActionCommand().contains(CharSecuence s)”,因为我来自 MVC 上下文,并且 Button 是在 View 类中声明的,但 actionPerformed 调用发生在控制器中。

      public View() {
          ....
          buttonPlus = new Button("+");
          buttonMinus = new Button("-");
          ....
      }
      
      public void addController(ActionListener controller) {
          buttonPlus.addActionListener(controller);
          buttonMinus.addActionListener(controller);
      }
      

      我的控制器类实现了 ActionListener,因此,在覆盖 actionPerformed 时:

      public void actionPerformed(ActionEvent e) {
          if(e.getActionCommand().contains("+")) {
              //do some action on the model
          } else if (e.getActionCommand().contains("-")) {
             //do some other action on the model
          }
      }
      

      我希望这个其他答案也有用。

      【讨论】:

        【解决方案11】:

        使用我的方法,您可以以“经典方式”编写按钮单击事件处理程序,就像您在 VB 或 MFC 中所做的那样;)

        假设我们有一个包含 2 个按钮的框架窗口类:

        class MainWindow {
            Jbutton searchButton;
            Jbutton filterButton;
        }
        

        您可以使用我的“路由器”类将事件路由回您的 MainWindow 类:

        class MainWindow {
            JButton searchButton;
            Jbutton filterButton;
            ButtonClickRouter buttonRouter = new ButtonClickRouter(this);
            
            void initWindowContent() {
                // create your components here...
                
                // setup button listeners
                searchButton.addActionListener(buttonRouter);
                filterButton.addActionListener(buttonRouter);
            }
        
            void on_searchButton() {
                // TODO your handler goes here...
            }
            
            void on_filterButton() {
                // TODO your handler goes here...
            }
        }
        

        你喜欢吗? :)

        如果你喜欢这种方式而讨厌 Java 的匿名子类方式,那么你和我一样老。 'addActionListener(new ActionListener {...})' 的问题在于它将所有按钮处理程序压缩到一个外部方法中,这使得程序看起来是有线的。 (如果您在一个窗口中有多个按钮)

        最后,路由器类在下面。您可以将其复制到您的程序中,无需任何更新。

        只有一件事要提一下:按钮字段和事件处理程序方法必须可以被这个路由器类访问!简单地说,如果你在程序的同一个包中复制这个路由器类,你的按钮字段和方法必须是包可访问的。否则,它们必须是公开的。

        import java.awt.event.ActionEvent;
        import java.awt.event.ActionListener;
        import java.lang.reflect.Field;
        import java.lang.reflect.InvocationTargetException;
        import java.lang.reflect.Method;
        
        public class ButtonClickRouter implements ActionListener {
            private Object target;
        
            ButtonClickRouter(Object target) {
                this.target = target;
            }
        
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                // get source button
                Object sourceButton = actionEvent.getSource();
        
                // find the corresponding field of the button in the host class
                Field fieldOfSourceButton = null;
                for (Field field : target.getClass().getDeclaredFields()) {
                    try {
                        if (field.get(target).equals(sourceButton)) {
                            fieldOfSourceButton = field;
                            break;
                        }
                    } catch (IllegalAccessException e) {
                    }
                }
        
                if (fieldOfSourceButton == null)
                    return;
        
                // make the expected method name for the source button
                // rule: suppose the button field is 'searchButton', then the method
                // is expected to be 'void on_searchButton()'
                String methodName = "on_" + fieldOfSourceButton.getName();
        
                // find such a method
                Method expectedHanderMethod = null;
                for (Method method : target.getClass().getDeclaredMethods()) {
                    if (method.getName().equals(methodName)) {
                        expectedHanderMethod = method;
                        break;
                    }
                }
        
                if (expectedHanderMethod == null)
                    return;
        
                // fire
                try {
                    expectedHanderMethod.invoke(target);
                } catch (IllegalAccessException | InvocationTargetException e) { }
            }
        }
        

        我是Java初学者(不是编程),所以上面的代码可能有什么不合适的地方。请在使用前检查一下。

        【讨论】:

          猜你喜欢
          • 2015-07-24
          • 2011-08-16
          • 1970-01-01
          • 1970-01-01
          • 2015-06-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多