双按钮双事件监听机制的简单java GUI

写在前面:
前两篇博客我们分别介绍了简单java GUI的基本结构及事件监听机制。这一次我们将介绍双事件(多事件)监听机制,并引入内部类。

1.设计任务

设计一个GUI,包含基本组件:按钮(两个),标签(一个),随机颜色圆-面板(一个),要求点击其中一个按钮可以改变标签文字,点击另一个按钮可以改变圆的颜色,实现双事件监听。

2.任务分析

本任务的难度在于双事件如何同时监听。我们已经知道,要实现事件监听,就必须实现ActionListener接口并具体实现actionPerformed方法。但是注意对于任何一个实现ActionListener接口的类而言,只能实现一个actionPerformed方法,那么如何对两个不同的按钮实施监听并且有不同的actionPerformed方法处理呢?我们使用内部类解决这个问题。内部类的形式如下所示:

class Outer {
    int outer_int;

    class Inner {
    int inner_int;
    }

使用内部类的一个好处是在内部类中,可以直接使用外部类中的属性和方法。我们用以下代码试图实现本设计任务。

3.代码Version1

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class newTwoButtons {
    JFrame frame;
    JLabel label;

    public static void main(String[] args) {
        newTwoButtons tb = new newTwoButtons();
        tb.go();
    }

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        label = new JLabel("I am waiting for you!");

        JButton labelButton = new JButton("Change a label");
        labelButton.addActionListener(new LabelListener());
        JButton circleButton = new JButton("Change a circle");
        circleButton.addActionListener(new CircleListener());

        MyDrawPanel myPanel = new MyDrawPanel();

        frame.getContentPane().add(BorderLayout.EAST, labelButton);
        frame.getContentPane().add(BorderLayout.SOUTH, circleButton);
        frame.getContentPane().add(BorderLayout.WEST, label);
        frame.getContentPane().add(BorderLayout.CENTER, myPanel);

        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    class LabelListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            label.setText("OhCh!");
        }
    }

    class CircleListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            frame.repaint();
        }
    }
}

class MyDrawPanel extends JPanel { 
    public void paintComponent(Graphics g) { // this method is called every time the button is clicked
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
        int red = (int) (Math.random() * 255);
        int green = (int) (Math.random() * 255);
        int blue = (int) (Math.random() * 255);
        Color randomColor = new Color(red, green, blue); 
        g.setColor(randomColor); // Set random color
        g.fillOval(70, 70, 100, 100); // Make a oval(circle)
    }
} 

4.结果测试

将窗口适当拉大以显示所有组件,如下图所示。
这里写图片描述

点击“change a circle”按钮,结果如下图所示:
这里写图片描述
说明“change a circle”按钮工作正常。

接下来点击“change a label”按钮,结果如下图所示:
这里写图片描述
我们看到,屏幕左方的label文字改变了,说明“change a label”按钮工作正常。不!等等!我们惊奇地发现圆形的颜色也改变了,可是我们并没有点击“change a circle”按钮啊!
为什么会这样呢?

5.问题在哪

其实,我们在尝试拉动窗口调整大小时,圆形的颜色也会改变,附图如下所示:
这里写图片描述
那么这里可以推断出当panel组件大小发生变化时(比如点击“change a label”按钮导致panel左侧区域展宽或者直接拉动窗口展宽),paintComponent函数就会执行。而我们希望的是,除了第一次生成图形之外,当且仅当点击“change a circle”按钮,才执行paintComponent函数。

6.解决方案

由以上分析我们可以初步给出一个方案:设置flag,当flag为false时不执行paintComponent函数,仅当其为true时才执行。给出代码如下所示:

7.代码Version2

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class newTwoButtons {
    JFrame frame;
    JLabel label;
    boolean flag = false;

    public static void main(String[] args) {
        newTwoButtons tb = new newTwoButtons();
        tb.go();
    }

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        label = new JLabel("I am waiting for you!");

        JButton labelButton = new JButton("Change a label");
        labelButton.addActionListener(new LabelListener());
        JButton circleButton = new JButton("Change a circle");
        circleButton.addActionListener(new CircleListener());

        flag = true;
        MyDrawPanel myPanel = new MyDrawPanel();

        frame.getContentPane().add(BorderLayout.EAST, labelButton);
        frame.getContentPane().add(BorderLayout.SOUTH, circleButton);
        frame.getContentPane().add(BorderLayout.WEST, label);
        frame.getContentPane().add(BorderLayout.CENTER, myPanel);

        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    class LabelListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            label.setText("OhCh!");
        }
    }

        class CircleListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            flag = true;
            frame.repaint();
        }
    }
        class MyDrawPanel extends JPanel { 
        public void paintComponent(Graphics g) { // this method is called every time the button is clicked
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
            if (flag == true) {
                int red = (int) (Math.random() * 255);
                int green = (int) (Math.random() * 255);
                int blue = (int) (Math.random() * 255);
                Color randomColor = new Color(red, green, blue); 
                g.setColor(randomColor); // Set random color
                g.fillOval(70, 70, 100, 100); // Make a oval(circle)
                flag = false;
            }
        }
    }
}

8.仍有问题?

测试发现,虽然现在只有点击按钮才能改变圆形的颜色,但是当改变窗口大小时,panel内圆形不显示(即paintComponent函数只执行了一个语句),这也并不是我们想要的结果。

9.最终方案

为了解决这个问题,我们的最终方案是使color成为公共变量,这样在扩展窗口时,依然执行paintComponent函数,但是绘图的颜色与上一次没有变化。代码如下所示:

10.代码Version3

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class newTwoButtons {
    JFrame frame;
    JLabel label;
    boolean flag = false;
    int red = 0, green = 0, blue = 0;

    public static void main(String[] args) {
        newTwoButtons tb = new newTwoButtons();
        tb.go();
    }

    public void go() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        label = new JLabel("I am waiting for you!");

        JButton labelButton = new JButton("Change a label");
        labelButton.addActionListener(new LabelListener());
        JButton circleButton = new JButton("Change a circle");
        circleButton.addActionListener(new CircleListener());

        flag = true;
        MyDrawPanel myPanel = new MyDrawPanel();

        frame.getContentPane().add(BorderLayout.EAST, labelButton);
        frame.getContentPane().add(BorderLayout.SOUTH, circleButton);
        frame.getContentPane().add(BorderLayout.WEST, label);
        frame.getContentPane().add(BorderLayout.CENTER, myPanel);

        frame.setSize(300, 300);
        frame.setVisible(true);
    }

    class LabelListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
            label.setText("OhCh!");
        }
    }

    class CircleListener implements ActionListener {
        public void actionPerformed(ActionEvent event) {
                flag = true;
                frame.repaint();
        }
    }

    class MyDrawPanel extends JPanel { 
        public void paintComponent(Graphics g) { // this method is called every time the button is clicked
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
            if (flag == true) {
                red = (int) (Math.random() * 255);
                green = (int) (Math.random() * 255);
                blue = (int) (Math.random() * 255);
                Color randomColor = new Color(red, green, blue); 
                g.setColor(randomColor); // Set random color
                g.fillOval(70, 70, 100, 100); // Make a oval(circle)
                flag = false;
            } else {
                Color randomColor2 = new Color(red, green, blue);
                g.setColor(randomColor2); // Set random color
                g.fillOval(70, 70, 100, 100); // Make a oval(circle)
            }
        }
    } 

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值