Post

Java: GridLayout + tic-tac-toe

這次的功課是使用 AWT(Abstract Windowing Toolkit) 寫出 3*3 的圈圈叉叉,本文將記下我撰寫途中遇到的問題

問題1:明明放在前面的 label 卻出現在整個頁面的最下面一列?

問題描述

我還在嘗試怎麼使用 grid 時,寫了以下的 Code,我先設定並 add label 後,利用迴圈跑出9個按鈕。
理論上 Label 應出現在按鈕上方,但他卻出現在底下。

java1 Label 出現在 button 後面

完整的 code 在這:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.awt.*;
public class hw1 extends Frame{
    static Frame frm = new Frame("Tic-Tac-Toe");
    static Label lab = new Label("It's \"O\" turn.");
    
    public static void main(String args[]){
        // 設定版面
        GridLayout grid = new GridLayout(4, 3);
        frm.setLayout(grid);
        frm.setSize(600, 800);
        frm.setBackground(Color.white);
        frm.setLocation(200, 50);
        
        // 設定標籤屬性
        lab.setBackground(Color.gray);
        lab.setFont(new Font("Serief",Font.BOLD,18));
        frm.add(lab);
        
        // 按鈕
        for(int i = 0; i < 9; i++){
            frm.add(new Button(), i);
        }
        
        frm.setVisible(true);
    }
}

為何如此?

原來問題出在第 21 行,frm.add(new Button(), i);
當 add 有兩個參數時,後面的 int 將會表示顯示的位置。(你可以在這裡找到官方文件的解說

例如我將它改為 frm.add(new Button(Integer.toString(i)), i + 1);,會顯示這樣的結果:
所有 button 從「第一格」(左上角的灰格為第0格)開始依序往下排列

修改成果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.awt.*;
public class hw1 extends Frame{
    static Frame frm = new Frame("Tic-Tac-Toe");
    static Label lab1 = new Label("It's \"O\" turn.");
    static Label lab2 = new Label();
    static Label lab3 = new Label();
    
    public static void main(String args[]){
        // 設定版面
        GridLayout grid = new GridLayout(4, 3);
        frm.setLayout(grid);
        frm.setSize(600, 800);
        frm.setBackground(Color.white);
        frm.setLocation(200, 50);
        
        // 設定標籤屬性,預留三個標籤的空位,分別是「該O下了」、「誰贏了」、「該X下了」
        lab1.setBackground(Color.gray);
        lab1.setFont(new Font("Serief",Font.BOLD,18));
        lab2.setBackground(Color.gray);
        lab2.setFont(new Font("Serief",Font.BOLD,18));
        lab3.setBackground(Color.gray);
        lab3.setFont(new Font("Serief",Font.BOLD,18));
        frm.add(lab1);
        frm.add(lab2);
        frm.add(lab3);
        lab2.setVisible(false);
        lab3.setVisible(false);
        
        
        // 按鈕
        for(int i = 0; i < 9; i++){
            frm.add(new Button(Integer.toString(i)), i + 3);
        }
        
        frm.setVisible(true);
    }
}

即會產生以下結果:
順利預留空間且按鈕正確排列


問題2:Button 無法使用 setText()?

在查資料的過程中發現,其實大家都用 JButton 而非 Button,這裡指的是 java.awt 中的 Button 喔。

問題描述

我想要實現「當按鈕被點擊時,按鈕中的文字顯示為 O」,所以將按鈕都加進 ActionListener(frm) 中,如下:

1
2
3
4
5
for(int i = 0; i < 9; i++){
            btn[i] = new Button();
            btn[i].addActionListener(frm);
            frm.add(btn[i], i + 3);
        }

我也新增了一個 function:

1
2
3
4
5
public void actionPerformed(ActionEvent e){
        Button actionBtn = (Button) e.getSource();
        btn[1].setText("O");
        
    }

他卻一直顯示這個問題:

1
2
3
4
5
6
7
D:\Java\hw1 ❯ javac .\hw1.java -encoding utf-8
.\hw1.java:52: error: cannot find symbol
        btn[1].setText("O");
              ^
  symbol:   method setText(String)
  location: class Button
1 error

為何如此?

這世界上沒有一個叫做 setText(),且傳入了一個 String 當參數的 function。
設定 Button 中的文字應用 setLabel(),而不是 setText()

不過確實還有其他 object 是用 setText(),這點需要多注意⚠

修改成果

按下按鈕後要觸發的 function 長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void actionPerformed(ActionEvent e){
        Button actionBtn = (Button) e.getSource();
        
        // 下棋!
        ++turn;
        if(turn % 2 != 0){
            lab1.setVisible(false);
            lab3.setVisible(true);
            actionBtn.setLabel("O");
        }
        else{
            lab1.setVisible(true);
            lab3.setVisible(false);
            actionBtn.setLabel("X");
        } 
    }

最後的成果:
可以正確顯示圈和叉


成品

經過一點波折,終於完成了小小的圈圈叉叉遊戲啦~
這是我第一次用視窗來設計程式,能夠和這種 GUI 互動的感覺真的成就感爆棚!

這裡是成果 demo:
終於像個圈圈叉叉了! 這裡是完整的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import java.awt.*;
import java.awt.event.*;

public class hw1 extends Frame implements ActionListener{
    static hw1 frm = new hw1();
    static Label lab1 = new Label("It's \"O\" turn.");
    static Label lab2 = new Label(); //win
    static Label lab3 = new Label("It's \"X\" turn."); // X
    static int turn = 0; // turn % 2 == 0 -> O, turn % 2 == 1 -> X
    static Button[] btn = new Button[9];
    
    public static void main(String args[]){
        // 設定版面
        GridLayout grid = new GridLayout(4, 3);
        frm.setLayout(grid);
        frm.setTitle("Tic-Tac-Toe");
        frm.setSize(600, 800);
        frm.setBackground(Color.white);
        frm.setLocation(200, 50);
        
        // 設定標籤屬性
        lab1.setBackground(Color.gray);
        lab1.setFont(new Font("Serief",Font.BOLD,18));
        lab2.setBackground(Color.gray);
        lab2.setFont(new Font("Serief",Font.BOLD,18));
        lab3.setBackground(Color.gray);
        lab3.setFont(new Font("Serief",Font.BOLD,18));
        frm.add(lab1);
        frm.add(lab2);
        frm.add(lab3);
        lab2.setVisible(false);
        lab3.setVisible(false);
        
        
        // 按鈕
        for(int i = 0; i < 9; i++){
            btn[i] = new Button();
            btn[i].addActionListener(frm);
            frm.add(btn[i], i + 3);
        }
        
        frm.setVisible(true);
        
        // 關閉視窗鍵被點下就結束程式
        frm.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e){System.exit(0);}
        });
    }
    
    public void actionPerformed(ActionEvent e){
        Button actionBtn = (Button) e.getSource();
        
        // 下棋!
        ++turn;
        if(turn % 2 != 0){
            lab1.setVisible(false);
            lab3.setVisible(true);
            actionBtn.setLabel("O");
        }
        else{
            lab1.setVisible(true);
            lab3.setVisible(false);
            actionBtn.setLabel("X");
        }
        
        // 判斷輸贏
        String[] game = new String[9];
        for(int i = 0; i < 9; ++i){
            game[i] = btn[i].getLabel();
        }
        
        boolean win = false;
        
        for(int i = 0; i < 3; ++i){
            // 橫列
            if(game[i * 3] == game[i * 3 + 1] && game[i * 3] == game[i * 3 + 2] && game[i * 3] != ""){
                win = true;
                break;
            }
            
            // 直行
            if(game[i] == game[i + 3] && game[i] == game[i + 6] && game[i] != ""){
                win = true;
                break;
            }
        }
        
        // 斜線
        if(game[0] == game[4] && game[0] == game[8] && game[0] != "")
            win = true;
        if(game[2] == game[4] && game[2] == game[6] && game[2] != "")
            win = true;
        
        // 輸出勝利訊息
        if(win == true){
            if(turn % 2 != 0){
                lab3.setVisible(false);
                lab2.setText("\"O\" win!");
                lab2.setVisible(true);
            }
            else{
                lab1.setVisible(false);
                lab2.setText("\"X\" win!");
                lab2.setVisible(true);
            }
        }
        else if(turn == 9){
            lab1.setVisible(false);
            lab3.setVisible(false);
            lab2.setText("Draw!");
            lab2.setVisible(true);
        }
        
    }
}
This post is licensed under CC BY 4.0 by the author.