一,MFC消息处理机制

WINDOWS 消息的种类:
  1、标准WINDOWS消息:这类消息是以WM_为前缀,不过WM_COMMAND例外。 例如: WM_MOVE、WM_QUIT等。
  2、命令消息:命令消息以WM_COMMAND为消息名。在消息中含有命令的标志符ID,以区分具体的命令。由菜单,工具栏等命令接口对象产生。
  3、控件通知消息:控件通知消息也是以WM_COMMAND为消息名。由编辑框、列表框和子窗口发送给父窗口的通知消息。在消息中包含控件通知码,以区分具体控件的通知消息。

 

 

MFC中处理消息的顺序

1.     AfxWndProc()接收消息,寻找消息所属的CWnd对象,然后调用AfxCallWndProc( )。

2.     AfxCallWndProc()存储消息(消息标识符和消息参数)供未来参考,然后调用WindowProc( )。

3.     WindowProc()发送消息给OnWndMsg( ),如果消息未被处理,则发送给DefWindowproc( )。

4.     OnWndMsg()首先按字节对消息进行排序,对于WM_COMMAND消息,调用OnCommand()消息响应函数;对于WM_NOTIFY消息调用OnNotify()消息响应函数。任何被遗漏的消息将是标准消息。OnWndMsg()函数搜索类的消息映像,以找到一个能处理任何窗口消息的处理函数。如果OnWndMsg()函数不能找到这样的处理函数的话,则把消息返回到WindowProc()函数,由它将消息发送给DefWindowProc()函数。

5.     OnCommand()查看这是不是一个控件通知(lParam参数不为NULL),如果它是,OnCommand()函数会试图将消息映射到制造通知的控件;如果它不是一个控件通知,或者控件拒绝映射的消息,OnCommand()就会调用OnCmdMsg()函数。

6.     OnNotify( )也试图将消息映射到制造通知的控件;如果映射不成功,OnNotify( )就调用相同的OnCmdMsg( )函数。

7.     根据接收消息的类,OnCmdMsg()函数将在一个称为命令传递(Command Routing)的过程中潜在的传递命令消息和控件通知。例如:如果拥有该窗口的类是一个框架类,则命令和控件通知消息也被传递到视图和文档类,并为该类寻找一个消息处理函数。

 

 

MFC中创建窗口的顺序

1.     PreCreateWindow()是一个重载函数,在窗口被创建前,可以在该重载函数中改变创建参数(可以设置窗口风格等等)。

2.     PreSubclassWindow()也是一个重载函数,允许首先子分类一个窗口OnGetMinMaxInfo()为消息响应函数,响应的是WM_GETMINMAXINFO消息,允许设置窗口的最大或者最小尺寸。

3.     OnNcCreate()也是一个消息响应函数,响应WM_NCCREATE消息,发送消息以告诉窗口的客户区即将被创建。

4.     OnNcCalcSize()也是消息响应函数,响应WM_NCCALCSIZE消息,作用是允许改变窗口客户区大小。

5.     OnCreate()也是消息响应函数,响应WM_CREATE消息,发送消息告诉一个窗口已经被创建。

6.     OnSize()也是消息响应函数,响应WM_SIZE消息,发送该消息以告诉该窗口大小已经发生变化。

7.     OnMove()也是消息响应函数,响应WM_MOVE消息,发送此消息说明窗口在移动。

8.     OnChildNotify()为重载函数,作为部分消息映射被调用,告诉父窗口即将被告知一个窗口刚刚被创建。

 

SetTimer与WM_TIMER消息

Timer被设置后会一直存在,直到用KillTimer(hwnd, ID)删除。

Timer被创建后,每隔一定时间会发送 WM_TIMER 消息,当收到WM_TIMER 消息后,程序就会调用函数。也可以在创建Timer时指定回调函数。

SetTimer 原形如下,注意 CWnd 类的 SetTimer 方法没有第一个参数:

UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse,  TIMERPROC lpTimerFunc);

四个参数分别是: hWnd 所属的窗口句柄,如果 lpTimerFunc 为空,这个窗口将接收 WM_TIMER 消息。

nIDEvent 计时器ID值,发送 WM_TIMER消息时会附带发送这个值。

uElapse 超时时间。每uElapse毫秒发送WM_TIMER

lpTimerFunc 回调函数,如果为空,将发送 WM_TIMER 消息给 hWnd 指定的窗口的消息队列。

lpTimerFunc 的函数原型如下:

VOID CALLBACK TimerProc( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);

 

用一个Windows窗口实例来解释消息处理

#include <windows.h>
#include <cstdio>

//回调函数
LRESULT CALLBACK WinSunProc(
    HWND hwnd,          // 窗口句柄
    UINT message,          // 消息代码
    WPARAM wParam,      // 附加参数
    LPARAM lParam
    ) {
        switch(message) {
        case WM_CREATE: {
            HDC hdc;
            PAINTSTRUCT ps;
            hdc = BeginPaint(hwnd, &ps);
            char str[20];
            wsprintf(str, "窗口创建消息");
            MessageBox(hwnd, str, "提示", MB_OK);
            break;
                        }
        case WM_PAINT: {
            SetTimer(hwnd, 10, 100, NULL);

            HDC hdc;
            PAINTSTRUCT ps;
            hdc = BeginPaint(hwnd, &ps);
            char str[20];
            wsprintf(str, "Hello World");
            TextOut(hdc, rand()%600, rand()%300, str, strlen(str));
            break;
                       }
        case WM_CHAR:
            char szChar[20];
            sprintf(szChar, "char code is %d", wParam);
            MessageBox(hwnd, szChar, "您的按键", 0);
            break;
        case WM_TIMER:
            InvalidateRect(hwnd, NULL, true);
            break;
        case WM_DESTROY: //销毁窗口
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
}

int WINAPI WinMain(
    HINSTANCE hInstance,         // handle to current instance
    HINSTANCE hPrevInstance,     // handle to previous instance
    LPSTR lpCmdLine,             // command line
    int nCmdShow                 // show state
    ) {
        // 窗口类
        WNDCLASS wndcls;
        wndcls.cbClsExtra = 0;                                          // 类附加内存
        wndcls.cbWndExtra = 0;                                          // 窗口附加内存
        wndcls.hbrBackground = (HBRUSH) GetStockObject(COLOR_WINDOW);    // 背景画刷句柄
        wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);                   // 窗口光标句柄
        wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);                       // 窗口图标句柄
        wndcls.hInstance = hInstance;                                   // 包含窗口过程函数的程序实例
        wndcls.lpfnWndProc = WinSunProc;                                // 只想窗口过程函数的指针
        wndcls.lpszClassName = "CRoot";                                // 窗口类名称
        wndcls.lpszMenuName = NULL;                                     // 菜单资源
        wndcls.style = CS_HREDRAW | CS_VREDRAW;                         // 窗口样式
        RegisterClass(&wndcls);

        // 创建窗口, 定义一个变量用来保存成功创建窗口后返回的句柄
        HWND hwnd  = CreateWindow(        // 窗口创建成功时返回为窗口分配的句柄 失败时返回NULL
            "CRoot",               // 窗口类名
            "Hello World",          // 窗口名字
            WS_CAPTION|WS_SYSMENU,    // 窗口样式
            300, 300,                   // 窗口左上角坐标
            700, 400,               // 窗口宽高
            NULL,                   // 父窗口句柄
            NULL,                   // 窗口菜单句柄
            hInstance,              // 窗口所属应用程序实例
            NULL                    // WM_CREATE消息附加参数lParam传入的数据指针
            );

        // 显示及刷新窗口
        ShowWindow(hwnd, SW_SHOWNORMAL);
        UpdateWindow(hwnd);

        // 定义消息结构体
        MSG msg;
        while (GetMessage(  // WM_QUIT消息返回0 错误返回-1
            &msg,           // 指向消息的结构体
            NULL,           // 指定接收属于哪一窗口的消息 通常设为NULL,用来接收属于调用线程的所有窗口的窗口消息
            0,              // 获取消息的最小值 通常为0
            0))             // 获取消息的最大值 都设为0表示接收所有消息
        {
            TranslateMessage(&msg);     // 将虚拟消息转换为字符消息 投递到调用线程的消息队列中 下次调用GetMessage时被取出
            DispatchMessage(&msg);      // 将消息传递给操作系统 由操作系统调用窗口过程函数对消息进行处理
        }
        return msg.wParam;
}

 

附加参数WPARAM与WM_COMMAND

创建一个按钮

CreateWindowEx(0,"Button","我是按钮",WS_VISIBLE|WS_CHILD,100,100,100,50,hwnd,(HMENU)10,0,0);

HMENU为按钮的ID号,监听器识别ID执行。

消息的附加信息在wParam中。

switch(message) {
        case WM_CREATE: {
            CreateWindowEx(0,"Button","我是按钮",WS_VISIBLE|WS_CHILD,100,100,100,50,hwnd,(HMENU)10,0,0);
            break;
                        }
        case WM_COMMAND: {
            WORD wmId    = LOWORD(wParam); 
            switch (wmId) {
            case (10):
                MessageBoxA(NULL,"Hello Button!","My TestProject",MB_OK);
                break;
            }
            break;
                         }

 

实例(数据结构课程设计)

MFC界面的贪吃蛇游戏

#include <iostream>
#include <windows.h>
#include <ctime>
#include <fstream>
#include <cstring>
#include <string>
#include <algorithm>
#include "resource.h"
using namespace std;

#define IDT_TIMER 100
#define HEIGHT 300
#define WIDTH 500

int POINT_X[2], POINT_Y[2];
int score = 0;
int rate = 100;
int change = 0;
struct People {
    char name[20];
    int num;
    bool operator < (const People &tmp) const {
        return num > tmp.num;
    }
} people[10];
int tot;
char user[20];

struct point {
    int x, y;
};
struct Node {
    point data;
    Node *next;
} top;
struct Link {
    Node *head, *last, *pre_last;
    Link() {
        last = head = ⊤
        last->next = NULL;
        last->data.x = 5;
        last->data.y = 5;
    }
} snake;
char direction = 'r';

void AddList() {
    Node *tmp = new Node;

    tmp->data.x = 1000;
    tmp->data.y = 1000;
    tmp->next = NULL;
    snake.pre_last = snake.last;
    snake.pre_last->next = tmp;
    snake.last = tmp;
}
void GoAhead(int dx, int dy) {
    snake.pre_last->next = NULL;
    snake.last->data.x = snake.head->data.x + dx;
    snake.last->data.y = snake.head->data.y + dy;
    snake.last->next = snake.head;
    snake.head = snake.last;
    snake.last = snake.pre_last;

    snake.pre_last = snake.head;
    while (snake.pre_last->next != snake.last)
        snake.pre_last = snake.pre_last->next;
}
void Drow(HWND hwnd) {
    HDC hdc;
    PAINTSTRUCT ps;
    hdc = BeginPaint(hwnd, &ps);
    RECT rc;
    //计分和静态文档
    char sz[20];
    wsprintf(sz, "分数:");
    TextOut(hdc, WIDTH + 20, 40, sz, strlen(sz));
    wsprintf(sz, "%d", score * 100);
    TextOut(hdc, WIDTH + 20, 70, sz, strlen(sz));
    wsprintf(sz, "排行榜:");
    TextOut(hdc, WIDTH + 20, 90, sz, strlen(sz));
    for(int i = 0; i<tot; i++) {
        wsprintf(sz, "%s     %d", people[i].name, people[i].num);
        TextOut(hdc, WIDTH + 20, 110 + i * 20, sz, strlen(sz));
    }
    //画蛇
    HBRUSH hBrush = CreateSolidBrush(RGB(0,0,0));
    Node *tp = snake.head;
    while (tp) {
        rc.left = (tp->data.x) * 11;
        rc.right = rc.left + 10;
        rc.top = (tp->data.y) * 11;
        rc.bottom = rc.top + 10;
        FillRect(hdc, &rc, hBrush);
        tp = tp->next;
    }
    //画点1
    hBrush = CreateSolidBrush(RGB(255,0,0));
    rc.left = POINT_X[0] * 11;
    rc.right = rc.left + 10;
    rc.top = POINT_Y[0] * 11;
    rc.bottom = rc.top + 10;
    FillRect(hdc, &rc, hBrush);
    //画点2
    rc.left = POINT_X[1] * 11;
    rc.right = rc.left + 10;
    rc.top = POINT_Y[1] * 11;
    rc.bottom = rc.top + 10;
    FillRect(hdc, &rc, hBrush);
    //画边界
    MoveToEx(hdc, WIDTH + 15, HEIGHT + 15, NULL);
    LineTo(hdc, WIDTH + 15, 0);
    DeleteObject(hBrush);
    EndPaint(hwnd, &ps);
}
int getPoint() {
    if (POINT_X[0] == (snake.head->data.x) && POINT_Y[0] == (snake.head->data.y))
        return 0;
    if (POINT_X[1] == (snake.head->data.x) && POINT_Y[1] == (snake.head->data.y))
        return 1;
    return -1;
}
int check() {
    if (snake.head->data.x
            < 0||(snake.head->data.x)*11>WIDTH||snake.head->data.y < 0||(snake.head->data.y)*11>HEIGHT)
        return 1;
    for (Node *p = snake.head->next; p->next; p = p->next)
        if (p->data.x == snake.head->data.x && p->data.y == snake.head->data.y)
            return 2;
    return 0;
}
bool isOK(int x, int y) {
    Node *tp = snake.head;
    while (tp) {
        if(tp->data.x == x && tp->data.y == y)
            return 1;
        tp = tp->next;
    }
    return 0;
}
//回调函数
LRESULT CALLBACK FUNC(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_CREATE: {
        tot = 0;
        ifstream inFile("score_list.txt");
        while (!inFile.eof()) {
            inFile >> people[tot].name >> people[tot].num;
            tot++;
        }
        SetTimer(hwnd, IDT_TIMER, rate, NULL);
        ifstream in("user.txt");
        in >> user;
        for(int i = 0; i<5; i++)
            AddList();
        break;
    }
    case WM_PAINT: { //窗口客户区需要重画
        srand((int)time(0));
        Drow(hwnd);
        change = getPoint();
        if (change >= 0) {
            AddList();
            score++;
            srand((int)time(0));
            do {
                POINT_X[change] = (rand() % WIDTH) / 11, POINT_Y[change] = (rand() % HEIGHT) / 11;
            } while(isOK(POINT_X[change], POINT_Y[change]));
        }
        int k = check();
        if (k) {
            if(k == 2) {
                KillTimer(hwnd, IDT_TIMER);
                MessageBox(NULL, "YOU LOSE !", "message", MB_OK);
                HDC hdc;
                PAINTSTRUCT ps;
                hdc = BeginPaint(hwnd, &ps);
                char str[20];
                wsprintf(str, "YOU LOSE !");
                TextOut(hdc, 400, 100, str, strlen(str));

                strcpy(people[tot].name, user), people[tot].num = score * 100;
                sort(people, people + tot + 1);
                if(tot > 5) tot = 5;
                ofstream inFile("score_list.txt");
                for(int i = 0; i < tot; i++) {
                    inFile << people[i].name << " " << people[i].num;
                }
                return 0;
            }
            int dx = -1, dy = -1;
            if(snake.head->data.x * 11 < WIDTH / 2)
                dx = 1;
            if(snake.head->data.y * 11 < HEIGHT / 2)
                dy = 1;
            if(direction == 'r')
                GoAhead(0, dy), GoAhead(-1, 0), direction = 'l';
            else if(direction == 'u')
                GoAhead(dx, 0), GoAhead(0, 1), direction = 'd';
            else if(direction == 'l')
                GoAhead(0, dy), GoAhead(1, 0), direction = 'r';
            else if(direction == 'd')
                GoAhead(dx, 0), GoAhead(0, -1), direction = 'u';
            //PostQuitMessage(0);
        }
        return 0;
    }
    case WM_TIMER:
        if (direction == 'l')
            GoAhead(-1, 0);
        else if (direction == 'r')
            GoAhead(1, 0);
        else if (direction == 'u')
            GoAhead(0, -1);
        else if (direction == 'd')
            GoAhead(0, 1);
        InvalidateRect(hwnd, NULL, true);
        break;

    case WM_KEYDOWN: {
        Node *tp = snake.head;
        {
            switch (LOWORD(wParam)) {
            case VK_UP:
                if (direction != 'd')
                    direction = 'u';
                break;
            case VK_DOWN:
                if (direction != 'u')
                    direction = 'd';
                break;
            case VK_RIGHT:
                if (direction != 'l')
                    direction = 'r';
                break;
            case VK_LEFT:
                if (direction != 'r')
                    direction = 'l';
                break;
            }
            InvalidateRect(hwnd, NULL, TRUE);
        }
        return 0;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nCmdShow) {
    WNDCLASSEX wndclass;

    // 用描述主窗口的参数填充WNDCLASSEX结构
    wndclass.cbSize = sizeof(wndclass);    // 结构的大小
    wndclass.style = CS_HREDRAW | CS_VREDRAW;    // 指定如果大小改变就重画
    wndclass.lpfnWndProc = FUNC;    // 窗口函数指针
    wndclass.cbClsExtra = 0;        // 没有额外的类内存
    wndclass.cbWndExtra = 0;        // 没有额外的窗口内存
    wndclass.hInstance = hInstance;        // 实例句柄
    wndclass.hIcon = LoadIcon(hInstance, (LPCTSTR) IDI_ICON1);    // 使用预定义图标
    wndclass.hCursor = LoadCursor(NULL,
                                  IDC_ARROW);        // 使用预定义的光标
    wndclass.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);     // 使用灰色背景画刷
    wndclass.lpszMenuName = NULL;        // 不指定菜单
    wndclass.lpszClassName = "MainWClass";    // 窗口类的名称
    wndclass.hIconSm = LoadIcon(hInstance, (LPCTSTR) IDI_ICON1);     // 没有类的小图标

    // 注册这个窗口类
    RegisterClassEx(&wndclass);

    // 创建主窗口
    HWND hwnd = CreateWindowEx(0,            // dwExStyle,扩展样式
                               "MainWClass",        // lpClassName,类名
                               "贪吃蛇",    // lpWindowName,标题
                               WS_OVERLAPPEDWINDOW & (~WS_SIZEBOX) & (~WS_MAXIMIZEBOX),// dwStyle,窗口风格
                               300,    //CW_USEDEFAULT,        // X,初始 X 坐标
                               300,    //CW_USEDEFAULT,        // Y,初始 Y 坐标
                               WIDTH + 150,    //CW_USEDEFAULT,        // nWidth,宽度
                               HEIGHT + 50,    //CW_USEDEFAULT,        // nHeight,高度
                               NULL,            // hWndParent,父窗口句柄
                               NULL,            // hMenu,菜单句柄
                               hInstance,        // hlnstance,程序实例句柄
                               NULL);            // lpParam,用户数据

    if (!hwnd) {
        MessageBox(NULL, "无法创建游戏窗口", "error", MB_OK);
        return -1;
    }
    srand((int)time(0));
    do {
        POINT_X[0] = (rand() % WIDTH) / 11, POINT_Y[0] = (rand() % HEIGHT) / 11;
    } while(isOK(POINT_X[0], POINT_Y[0]));
    do {
        POINT_X[1] = (rand() % WIDTH) / 11, POINT_Y[1] = (rand() % HEIGHT) / 11;
    } while(isOK(POINT_X[1], POINT_Y[1]));

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg;
    while ( GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        //将消息发送到回调函数
        DispatchMessage(&msg);
    }
    //当GetMessage返回0时程序结束
    return msg.wParam;
}

 

二,JAVA GUI消息

toggle.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {}
}

对每一个控件设置事件。

或,implements ActionListener 实现ActionListener接口

public void actionPerformed(ActionEvent e) {
  if (e.getSource() == btnCalc) {}
  if (e.getSource() == btnCalc2) {}
}

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.*;

class NumberFrame extends JFrame implements ActionListener {
    JTextField txtStart;
    JTextField txtEnd;
    JTextField txtFactor;
    JTextArea txtRes;
    JButton btnCalc, btnCalc2;
    JPanel inputPanel;
    JScrollPane resultPane;
    JPanel panel;

    NumberFrame(String title) {
        super(title);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(400, 150);
        setLocation(300, 200);
        setVisible(true);
        Container cp = getContentPane();

        panel = new JPanel();
        panel.setLayout(new BorderLayout());

        JPanel lblPane = new JPanel();
        lblPane.setLayout(new GridLayout(4, 1));
        lblPane.add(new JLabel("From"));
        lblPane.add(new JLabel("To"));
        lblPane.add(new JLabel("Factor"));
        lblPane.add(new JLabel());

        JPanel txtPane = new JPanel();
        txtPane.setLayout(new GridLayout(4, 1)); // 划分成4行1列
        txtStart = new JTextField();
        txtEnd = new JTextField();
        txtFactor = new JTextField();
        txtPane.add(txtStart);
        txtPane.add(txtEnd);
        txtPane.add(txtFactor);
        btnCalc = new JButton("Calculate");
        btnCalc.addActionListener(this);
        txtPane.add(btnCalc);

        inputPanel = new JPanel();
        inputPanel.setLayout(new BorderLayout());
        inputPanel.add(lblPane, BorderLayout.WEST);
        inputPanel.add(txtPane, BorderLayout.CENTER);

        panel.add(inputPanel, BorderLayout.WEST);

        // result panel
        txtRes = new JTextArea(10, 10);
        txtRes.setLineWrap(true);
        resultPane = new JScrollPane(txtRes);
        panel.add(resultPane, BorderLayout.CENTER);

        btnCalc2 = new JButton("clean");
        btnCalc2.addActionListener(this);
        panel.add(btnCalc2, BorderLayout.SOUTH);

        cp.add(panel);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == btnCalc) {
            txtRes.setText(null);
            try {
                int m = Integer.parseInt(txtStart.getText().trim());
                int n = Integer.parseInt(txtEnd.getText().trim());
                int factor = Integer.parseInt(txtFactor.getText().trim());

                for (int i = m; i <= n; ++i) {
                    if (i % factor == 0)
                        txtRes.append(String.valueOf(i) + " ");
                }
            } catch (NumberFormatException e1) {
                e1.printStackTrace();
            }
        }
        if (e.getSource() == btnCalc2) {
            txtRes.setText(null);
        }
    }
}

class TestButtons extends JFrame {
    JButton jButton = new JButton("JButton"); // 按钮
    JToggleButton toggle = new JToggleButton("Toggle Button"); // 切换按钮
    JCheckBox checkBox = new JCheckBox("Check Box"); // 复选按钮
    JRadioButton radio1 = new JRadioButton("Radio Button 1"); // 单选按钮
    JRadioButton radio2 = new JRadioButton("Radio Button 2");
    JRadioButton radio3 = new JRadioButton("Radio Button 3");
    JLabel label = new JLabel("Here is Status, look here."); // 不是按钮,是静态文本

    public TestButtons() {
        super("Test");
        setVisible(true);
        setLocation(750, 200);
        setSize(200, 250);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        getContentPane().setLayout(new java.awt.FlowLayout());

        // 为一般按钮添加动作监听器
        jButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                label.setText("You clicked jButton");
            }
        });

        // 为切换按钮添加动作监听器
        toggle.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JToggleButton toggle = (JToggleButton) e.getSource();
                if (toggle.isSelected()) {
                    label.setText("You selected Toggle Button");
                } else {
                    label.setText("You deselected Toggle Button");
                }
            }
        });

        // 为复选按钮添加条目监听器
        checkBox.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                JCheckBox cb = (JCheckBox) e.getSource();
                label.setText("Selected Check Box is " + cb.isSelected());
            }
        });

        // 将单选按钮添加到按钮组中,按钮组中只能选一个
        ButtonGroup group = new ButtonGroup();
        group.add(radio1);
        group.add(radio2);
        group.add(radio3);

        // 生成一个新的动作监听器对象,备用
        ActionListener al = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JRadioButton radio = (JRadioButton) e.getSource();
                if (radio == radio1) {
                    label.setText("You selected Radio Button 1");
                } else if (radio == radio2) {
                    label.setText("You selected Radio Button 2");
                } else {
                    label.setText("You selected Radio Button 3");
                }
            }
        };
        // 为各单选按钮添加动作监听器
        radio1.addActionListener(al);
        radio2.addActionListener(al);
        radio3.addActionListener(al);

        getContentPane().add(jButton);
        getContentPane().add(toggle);
        getContentPane().add(checkBox);
        getContentPane().add(radio1);
        getContentPane().add(radio2);
        getContentPane().add(radio3);
        getContentPane().add(label);

    }
}

public class UI {
    public static void main(String[] args) {
        new NumberFrame("Number Game");
        new TestButtons();
    }
}