5

我最近在做一个编程任务,要求我们在代码中实现一个由 UML 图指定的程序。有一次,该图指定我必须创建一个匿名 JButton,它显示一个计数(从 1 开始)并在每次单击时递减。JButton 及其 ActionListener 都必须是匿名的。

我想出了以下解决方案:

public static void main(String[] args) {
  JFrame f = new JFrame("frame");
  f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  f.setSize(400, 400);

  f.getContentPane().add(new JButton() {

    public int counter;

    {
      this.counter = 1;
      this.setBackground(Color.ORANGE);
      this.setText(this.counter + "");

      this.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent arg0) {
          counter --;
          setText(counter + "");
        }
      });

    }
  });

  f.setVisible(true);

}

这将添加一个匿名 JButton,然后添加另一个(内部)匿名 ActionListener 来处理事件并根据需要更新按钮的文本。有更好的解决方案吗?我很确定我不能声明匿名JButton implements ActionListener (),但是还有另一种更优雅的方式来实现相同的结果吗?

4

6 回答 6

13

我通常是这样的:

JPanel panel = new JPanel();
panel.add(new JButton(new AbstractAction("name of button") {
    public void actionPerformed(ActionEvent e) {
        //do stuff here
    }
}));

AbstractAction 实现了 ActionListener 所以这应该满足任务。

将这么多行代码压缩在一起可能是一种不好的做法,但是如果您习惯阅读它,那么它可能会非常优雅。

于 2009-05-21T04:33:58.000 回答
4

这很丑陋,但您可以使用 ActionListener 方法和匿名类执行以下操作:

  f.getContentPane().add(new JButton(new AbstractAction("name of button") {
      private int counter = 0;

      public void actionPerformed(ActionEvent e) {
          ((JButton) e.getSource()).setText(Integer.toString(counter--));
      }
  }) {
      {
          setText("1");
      }
  });

为了更容易访问计数器,您可以将其移到类的顶层并从调用 setText 的两个位置访问它。

于 2009-05-21T10:59:12.213 回答
2

实现多种类型通常是一个坏主意。

很少需要扩展JComponent类,尽管很多糟糕的软件和教程都这样做了。最近流行的一个习惯用法/hack 是 Double Brace - 一个类只是子类,以便给它一个实例初始化器,它的作用就像with来自其他语言的语句。

在这种情况下,相关代码可以写成:

JButton button = new JButton();
button.addActionListener(new ActionListener() {
    int counter = 1;
    {
        updateText();
    }
    public void actionPerformed(ActionEvent arg0) {
        --counter;
        updateText();
    }
    private void updateText()
        setText(Integer.toString(counter));
    }
});
f.getContentPane(button);

如果它变得更复杂,那么您可能想要创建一个外部类(不实现ActionListener或扩展JButton)来处理数据。

另请注意,您应该使用EventQueue.invokeLater样板文件来确保 Swing 组件只在 AWT EDT 上使用过。

于 2009-05-21T10:29:22.693 回答
1

我不会在现实世界的程序中做这样的事情,但考虑到你的作业要求,你很难做得更好。

于 2009-05-21T04:41:50.937 回答
1

那么有一个更优雅的方式来做到这一点。

不幸的是,它不是核心 Java/Swing 方法。

您可以使用 Groovy 中的 SwingBuilder 来实现相同的结果,使用稍微简洁的语法,例如伪代码:

button(text: '' + counter,
         actionPerformed: {counter--; text = '' + counter + ''},
         constraints:BL.SOUTH)

[ http://groovy.codehaus.org/Swing+Builder][1]

不过,我不会在你的作业中使用它,我已经看到学生真的偏离了规范并因此被扣分,但至少你可以将它作为进一步调查的可能途径。

我认为你目前拥有的绝对没问题。

于 2009-05-21T04:43:53.603 回答
0

这是仅在家庭作业中被迫完成的不良实践任务之一;-) 坏事:

  • 使用 ActionListener 而不是本身不好的 Action
  • 结果,范围界定问题冒了出来
    • 计数器的范围超出了必要的范围
    • 需要访问 actionPerformed 中的按钮(通过类型转换或访问周围对象的 api)
  • 不可读(又名:不可维护)的代码

但是,那么..我们无法抗拒,我们可以吗 ;-) 这是一个使用 Action 的版本,对于前两个问题来说是干净的(或者我认为是这样的),与所有其他示例一样不可读(当然,我作弊了:首先实现匿名类,然后让 IDE 执行内联

    f.add(new JButton(new AbstractAction() {

        int counter = 1;
        { // constructor block of action
            updateName();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            counter--;
            updateName();
        }

        private void updateName() {
            putValue(Action.NAME, "" + counter);
        }

    })  { // subclass button 
          {  // constructor block button
            setBackground(Color.PINK);
        }}
    );
于 2011-05-04T16:16:49.373 回答