0

Series Java Swing Bài 15: JCheckBox & JRadioButton: Quản lý lựa chọn với ButtonGroup

Trong các form thu thập dữ liệu (như form khảo sát, cấu hình phần mềm, bộ lọc tìm kiếm), ngoài việc nhập chữ, chúng ta thường xuyên bắt gặp các tùy chọn đánh dấu (tick/check). Java Swing cung cấp 2 Component chuyên biệt cho việc này, kế thừa chung từ nhóm JToggleButton: JCheckBox và JRadioButton.

1. JCheckBox: Ô vuông đa lựa chọn (Multi-select)

JCheckBox có giao diện là một hình vuông nhỏ. Đặc điểm cốt lõi của nó là tính độc lập: Việc bạn tích chọn ô vuông này hoàn toàn không ảnh hưởng đến trạng thái của ô vuông bên cạnh. Do đó, nó sinh ra để đại diện cho các tùy chọn dạng "Có hoặc Không", hoặc chọn nhiều đáp án cùng lúc.

Java

// Khởi tạo CheckBox mặc định chưa được chọn
JCheckBox chkMusic = new JCheckBox("Nghe nhạc");

// Khởi tạo CheckBox và đánh dấu tick (chọn) sẵn
JCheckBox chkSport = new JCheckBox("Chơi thể thao", true);

Các hàm kiểm tra trạng thái quan trọng:

  • isSelected(): Trả về true nếu ô đang được tích dấu V, false nếu đang để trống. (Cực kỳ quan trọng khi bạn bấm nút Submit form để lưu dữ liệu).

  • setSelected(boolean b): Dùng code để ép nó đổi trạng thái tích chọn hoặc bỏ tích.

2. JRadioButton: Hình tròn lựa chọn duy nhất (Single-select)

JRadioButton có giao diện là một hình tròn. Theo bản chất của thiết kế UI/UX trên toàn thế giới, RadioButton được dùng khi bạn chỉ được phép chọn 1 và chỉ 1 đáp án trong một nhóm. (Ví dụ: Giới tính Nam/Nữ/Khác, hoặc Hình thức thanh toán: Tiền mặt/Chuyển khoản).

Tuy nhiên, nếu bạn chỉ khởi tạo 3 cái JRadioButton và ném thẳng lên giao diện, bạn sẽ phát hiện ra một sự thật "đau lòng": Bạn có thể click chọn cả 3 cái hình tròn đó cùng lúc!

Lý do là bản thân các JRadioButton không hề biết chúng thuộc về cùng một nhóm với nhau. Chúng ta cần một "Người quản lý".

3. "Người Quản Lý" ButtonGroup

ButtonGroup không phải là một Component đồ họa (bạn không thể add(buttonGroup) vào JFrame). Nó là một lớp logic ngầm chạy bên dưới.

Khi bạn gom các JRadioButton vào chung một ButtonGroup, nó sẽ thực thi một quy tắc thép: Mỗi khi một thành viên trong nhóm được chọn, nó sẽ tự động chạy đi gỡ bỏ dấu tick của tất cả các thành viên còn lại.

Java

// 1. Tạo các RadioButton
JRadioButton radMale = new JRadioButton("Nam");
JRadioButton radFemale = new JRadioButton("Nữ");
JRadioButton radOther = new JRadioButton("Khác");

// Đánh dấu chọn sẵn một nút làm mặc định
radMale.setSelected(true); 

// 2. Tạo Group quản lý
ButtonGroup genderGroup = new ButtonGroup();

// 3. Nạp các nút vào Group (Logic)
genderGroup.add(radMale);
genderGroup.add(radFemale);
genderGroup.add(radOther);

// 4. Thêm các nút lên giao diện (UI)
frame.add(radMale);
frame.add(radFemale);
frame.add(radOther);

Mẹo: ButtonGroup cũng có thể gom nhóm các JCheckBox. Khi đó, các ô vuông CheckBox sẽ hoạt động y hệt RadioButton (chỉ được chọn 1 ô vuông). Tuy nhiên, cách làm này phá vỡ quy ước UI/UX chung, gây bối rối cho người dùng, nên bạn tuyệt đối tránh sử dụng kiểu đó.

4. Bắt sự kiện thay đổi trạng thái ngay lập tức (ItemListener)

Thay vì chờ người dùng bấm nút "Lưu dữ liệu" rồi mới dùng isSelected() để kiểm tra, đôi khi bạn muốn ứng dụng phản hồi ngay lập tức khi người dùng vừa tích vào một ô (Ví dụ: Tích vào ô "Giao hàng hỏa tốc" thì giá tiền nhảy lên ngay lập tức).

Lúc này, bạn không dùng ActionListener (vì nó không phân biệt được là đang tích vào hay đang gỡ tích ra), mà dùng ItemListener.

Java

chkExpressDelivery.addItemListener(e -> {
    if (e.getStateChange() == ItemEvent.SELECTED) {
        System.out.println("Cộng thêm 50k phí ship!");
    } else if (e.getStateChange() == ItemEvent.DESELECTED) {
        System.out.println("Trừ đi 50k phí ship.");
    }
});

5. Source Code Thực Chiến: Form Khảo Sát Tích Hợp

Đoạn mã sau sử dụng BoxLayout dọc kết hợp với các Panels con (FlowLayout ngang) để tạo ra một Form khảo sát nhỏ bao gồm cả JRadioButton (Giới tính) và JCheckBox (Sở thích).

Java

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

public class CheckBoxRadioDemo extends JFrame {

    public CheckBoxRadioDemo() {
        setTitle("Form Khảo Sát - Bài 15");
        setSize(400, 350);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        
        // Layout chính: Xếp dọc từ trên xuống dưới, cách lề 15px
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));

        // ==========================================
        // KHU VỰC 1: JRadioButton (Chọn 1) - Giới tính
        // ==========================================
        JLabel lblGender = new JLabel("1. Giới tính của bạn là:");
        lblGender.setFont(new Font("Arial", Font.BOLD, 14));
        mainPanel.add(lblGender);

        JPanel genderPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        JRadioButton radMale = new JRadioButton("Nam");
        JRadioButton radFemale = new JRadioButton("Nữ", true); // Mặc định chọn Nữ
        JRadioButton radOther = new JRadioButton("Không tiết lộ");

        // GOM NHÓM ĐỂ ĐẢM BẢO CHỈ CHỌN 1
        ButtonGroup bgGender = new ButtonGroup();
        bgGender.add(radMale);
        bgGender.add(radFemale);
        bgGender.add(radOther);

        genderPanel.add(radMale);
        genderPanel.add(radFemale);
        genderPanel.add(radOther);
        mainPanel.add(genderPanel);

        mainPanel.add(Box.createRigidArea(new Dimension(0, 15))); // Tạo khoảng trống

        // ==========================================
        // KHU VỰC 2: JCheckBox (Chọn nhiều) - Sở thích
        // ==========================================
        JLabel lblHobbies = new JLabel("2. Sở thích (Chọn nhiều):");
        lblHobbies.setFont(new Font("Arial", Font.BOLD, 14));
        mainPanel.add(lblHobbies);

        JPanel hobbiesPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        JCheckBox chkCode = new JCheckBox("Lập trình");
        JCheckBox chkGame = new JCheckBox("Chơi Game");
        JCheckBox chkRead = new JCheckBox("Đọc sách");

        hobbiesPanel.add(chkCode);
        hobbiesPanel.add(chkGame);
        hobbiesPanel.add(chkRead);
        mainPanel.add(hobbiesPanel);

        // Bắt sự kiện đổi màu chữ ngay khi click (dùng ItemListener)
        chkCode.addItemListener(e -> {
            if (e.getStateChange() == ItemEvent.SELECTED) {
                chkCode.setForeground(Color.RED); // Tích vào thì đổi chữ đỏ
            } else {
                chkCode.setForeground(Color.BLACK); // Bỏ tích thì chữ đen
            }
        });

        mainPanel.add(Box.createVerticalGlue()); // Đẩy nút Submit xuống dưới

        // ==========================================
        // KHU VỰC 3: Nút Xác Nhận
        // ==========================================
        JButton btnSubmit = new JButton("Gửi Kết Quả");
        mainPanel.add(btnSubmit);

        // Xử lý sự kiện bấm nút
        btnSubmit.addActionListener(e -> {
            StringBuilder result = new StringBuilder("Kết quả khảo sát:\n");
            
            // Xử lý Radio: Do RadioGroup không có hàm getSelectedText(), ta phải tự check
            String gender = "Chưa rõ";
            if (radMale.isSelected()) gender = "Nam";
            else if (radFemale.isSelected()) gender = "Nữ";
            else if (radOther.isSelected()) gender = "Không tiết lộ";
            result.append("- Giới tính: ").append(gender).append("\n");

            // Xử lý CheckBox
            result.append("- Sở thích: ");
            if (chkCode.isSelected()) result.append("Lập trình, ");
            if (chkGame.isSelected()) result.append("Game, ");
            if (chkRead.isSelected()) result.append("Đọc sách, ");

            JOptionPane.showMessageDialog(this, result.toString());
        });

        add(mainPanel);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new CheckBoxRadioDemo().setVisible(true));
    }
}

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí