雙按鈕雙事件監聽機制的簡單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)
            }
        }
    } 

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章