如何添加侦听多个按钮的动作侦听器

Posted

技术标签:

【中文标题】如何添加侦听多个按钮的动作侦听器【英文标题】: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 【参考方案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");
    
  

【讨论】:

DontExtendJFrameAndDontImplementXXListenerAtTopLevel :-) 为什么不扩展 JFrame?我一直被教导要这样做。 @Lucas 一般来说,人们应该更喜欢组合而不是继承。 @Lucas 请参阅en.wikipedia.org/wiki/Composition_over_inheritance、***.com/questions/49002/… 和my.safaribooksonline.com/book/programming/java/9780137150021/… 我建议购买和阅读最后一个链接中的Effective Java 这本书。【参考方案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初学者(不是编程),所以上面的代码可能有什么不合适的地方。请在使用前检查一下。

【讨论】:

以上是关于如何添加侦听多个按钮的动作侦听器的主要内容,如果未能解决你的问题,请参考以下文章

在 IOS 中以编程方式设置动作侦听器

单选按钮上的动作侦听器

将动作侦听器方法作为标记文件属性传递

如何将侦听器添加到 osx 上的 java 停靠图标

来自另一个类的 JButton 动作侦听器

3个按钮的通用动作监听器