如何添加监听多个按钮的动作事件?

29

我正在尝试弄清楚我的事件监听器(action listeners)哪里出错了。我按照多个教程操作,但是在使用事件监听器时,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
  1. 类名应该是每个单词的首字母大写。2) 除非添加功能,否则不要扩展框架。3) 如果扩展框架,则可能不需要在 main(String[]) 中实例化一个框架。4) 通常认为最好为每个需要的 GUI 元素添加一个动作监听器,而不是使用一个巨大的 if/else 级联的 actionPerformed(ActionEvent) 方法。5) 如果遵循第四条建议,问题基本上就消失了。
- Andrew Thompson
可以使用操作(Actions) - 对于许多执行相同操作的UI元素(例如工具栏和按钮),保持一致性是最佳选择。请参见http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html - tgkprog
11个回答

41

静态方法中没有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)
    }  

}

太好了,这解决了我的疑惑。感谢您的帮助。 - user519670

22

我很惊讶没有人提到使用动作命令(Action Command)。这是一种将源和监听器关联起来的标准方式。如果:

  • 您有多个事件源需要执行相同的操作(例如,如果您希望用户可以在文本字段上按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");
    }
  }
}

4
请勿扩展JFrame并避免在顶层实现XX监听器 :-) - kleopatra
为什么不应该扩展JFrame?我一直被教导要这样做。 - Lucas
4
通常情况下,应该优先使用组合(composition)而非继承(inheritance)。 - Qwerky
1
@Lucas 请参考 http://en.wikipedia.org/wiki/Composition_over_inheritance、https://dev59.com/KXVD5IYBdhLWcg3wNY5Z 和 http://my.safaribooksonline.com/book/programming/java/9780137150021/classes-and-interfaces/ch04lev1sec4。 我建议购买并阅读最后一个链接中的书籍 Effective Java - Qwerky

10

这是根据我的评论修改过的源代码表单。请注意,GUI应该在EDT上构建和更新,尽管我没有做到那一步。

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代码都是垃圾,结果发现是开发人员太糟糕了。 - drakonli
1
这个9年前的问题仍然有帮助。 - SHikha Mittal
很高兴能帮到你,@SHikhaMittal! :) - Andrew Thompson
顺便说一下,我是新手,4个月前开始学习Java,我需要帮助,这个堆栈溢出网站对初学者不友好,虽然我一直在从YouTube等错误的途径学习,你能推荐任何可以建立我的基础的资源吗? - SHikha Mittal
1
@SHikhaMittal 我推荐由Java语言提供者本身提供的Java教程。这就是我学习Java的方式(当然还有成千上万的问题和答案在这里)。 - Andrew Thompson

3
问题在于button1是一个局部变量。你可以通过修改添加actionListener的方式来解决这个问题。
button.addActionListener(new ActionListener() {  
            public void actionPerformed(ActionEvent e)
            {
                //button is pressed
                System.out.println("You clicked the button");
            }});

或者你可以将button1设置为全局变量。


非常感谢您的帮助,这为我解答了许多问题。 - user519670

3

你已经知道如何解决你的问题,但是我认为这里还有更重要的问题。

  • 坚持惯例。即使是一次性的代码也要遵循命名类的初始用例。

  • 不需要就不要扩展类。很少有情况需要扩展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


2

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

有两种流行的方法。

使用公共的动作监听器

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

Original Answer翻译成"最初的回答"

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来调用它:
最初的回答:

使用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;
    }
}

请查看JFrame按钮、监听器和字段以获取更多信息。

最初的回答

1

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

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

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


非常感谢您的帮助,这为我解答了许多问题。 - user519670

0
你在 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)
}

非常感谢您的帮助,这为我回答了很多问题。 - user519670

0
首先,使用super()和构造函数适当地扩展JFrame, 然后将actionlisteners添加到框架并添加按钮。
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);
    }
}

不要扩展JFrame,而是使用它;不要实现xxListener,而是使用它。除非你真正运行了示例并注意到没有使用Calc,否则不要使用它。 - kleopatra

0

我使用"e.getActionCommand().contains(CharSecuence s)",因为我来自MVC上下文,而按钮在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
    }
}

希望这个答案也有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接