Series Java Swing Bài 10: Trùm cuối GridBagLayout - Thiết kế Form phức tạp và Responsive
Nếu bạn đã trải qua FlowLayout (chạy lung tung), GridLayout (cứng nhắc ép đều các ô), và BorderLayout (chỉ có 5 vùng), chắc chắn bạn sẽ gặp khó khăn khi muốn thiết kế một Form nhập liệu thực tế: nơi mà nhãn (Label) thì ngắn, ô nhập (TextField) thì dài, có trường chiếm 1 cột, có trường chiếm 2 cột (như ô Địa chỉ).
Để giải bài toán này, Java cung cấp Layout mạnh mẽ và phức tạp nhất: GridBagLayout.
1. GridBagLayout Là Gì?
GridBagLayout chia không gian thành một bảng lưới (grid) giống như GridLayout. Tuy nhiên, nó sở hữu 2 siêu năng lực mà GridLayout không có:
- Gộp ô (Spanning): Một Component có thể chiếm nhiều cột (colspan) hoặc nhiều hàng (rowspan) — giống y hệt tính năng Merge Cell trong Excel.
- Trọng lượng co giãn (Weight): Bạn có thể chỉ định chính xác cột nào sẽ phình to ra, cột nào giữ nguyên kích thước khi người dùng kéo giãn cửa sổ.
2. Bí Kip Làm Chủ: Lớp GridBagConstraints (GBC)
Linh hồn của GridBagLayout không nằm ở bản thân nó, mà nằm ở lớp GridBagConstraints. Bạn không thể chỉ ném Component vào Layout bằng lệnh add(btn) như bình thường, mà bạn phải "gói" Component đó kèm theo một tờ giấy hướng dẫn (chính là đối tượng GridBagConstraints).
Dưới đây là 5 thuộc tính sống còn của tờ giấy hướng dẫn GridBagConstraints:
| Thuộc tính | Ý nghĩa | Ví dụ thực tế |
|---|---|---|
gridx / gridy |
Tọa độ đặt Component. Tính từ góc trên bên trái (0, 0). | gridx = 1, gridy = 0 (Cột 2, hàng 1). |
gridwidth / gridheight |
Số cột / số hàng mà Component này chiếm dụng (Gộp ô). | gridwidth = 2 (Ô này chiếm độ rộng 2 cột). |
fill |
Nếu vùng chứa lớn hơn Component, Component có được phép phình to ra để lấp đầy không? | fill = GridBagConstraints.HORIZONTAL (Chỉ phình theo chiều ngang). |
weightx / weighty |
Xác định "Trọng lượng co giãn". Khi cửa sổ to ra, ai sẽ ăn phần không gian thừa? (Từ 0.0 đến 1.0) | weightx = 1.0 (Ô Text phình to hết cỡ), weightx = 0.0 (Ô Label giữ nguyên). |
insets |
Khoảng cách margin (khoảng trắng ngoài) giữa Component này với các ô xung quanh. | insets = new Insets(top, left, bottom, right). |
3. Quy Tắc "Weight" - Bí Mật Của Form Responsive
Đây là quy tắc cực kỳ quan trọng đối với các bạn làm Backend, nơi Form thường có:
- Cột bên trái (chứa
JLabel- Tiêu đề): Phải giữ nguyên kích thướcweightx = 0.0. - Cột bên phải (chứa
JTextField- Nhập liệu): Phải phình to ra bám theo mép phải cửa sổweightx = 1.0.
Nếu bạn quên set weightx = 1.0 cho JTextField, toàn bộ form của bạn sẽ co cụm lại ở giữa màn hình rất xấu.
4. Source Code Thực Chiến: Thiết Kế Form Đăng Ký
Dưới đây là một mẫu Form nhập liệu chuẩn xác với 3 hàng:
- Hàng 1: Họ tên (Label 1 cột, TextField 1 cột).
- Hàng 2: Email (Label 1 cột, TextField 1 cột).
- Hàng 3: Nút "Lưu" nằm ở hàng 3 chiếm trọn 2 cột.
import javax.swing.*;
import java.awt.*;
public class GridBagLayoutDemo extends JFrame {
public GridBagLayoutDemo() {
setTitle("Form Đăng Ký - GridBagLayout");
setSize(450, 250);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// Khởi tạo Layout
setLayout(new GridBagLayout());
// Tạo tờ giấy hướng dẫn (Constraints)
GridBagConstraints gbc = new GridBagConstraints();
// Thiết lập margin chung cho tất cả các Component: Top 5, Left 5, Bottom 5, Right 5
gbc.insets = new Insets(5, 5, 5, 5);
// ==========================================
// DÒNG 1: HỌ VÀ TÊN
// ==========================================
// Cột 1: Nhãn "Họ và tên"
gbc.gridx = 0; // Cột 0
gbc.gridy = 0; // Hàng 0
gbc.gridwidth = 1; // Chiếm 1 cột
gbc.weightx = 0.0; // Trọng lượng 0: Không co giãn khi kéo cửa sổ
gbc.fill = GridBagConstraints.NONE; // Kích thước tự nhiên
gbc.anchor = GridBagConstraints.EAST; // Căn sát lề phải của ô
add(new JLabel("Họ và tên:"), gbc);
// Cột 2: Ô nhập "Họ và tên"
gbc.gridx = 1; // Cột 1
gbc.gridy = 0; // Hàng 0
gbc.weightx = 1.0; // Trọng lượng 1.0: Ăn toàn bộ không gian thừa theo chiều ngang
gbc.fill = GridBagConstraints.HORIZONTAL; // Phình to theo chiều ngang
add(new JTextField(20), gbc);
// ==========================================
// DÒNG 2: EMAIL
// ==========================================
// Cột 1: Nhãn "Email"
gbc.gridx = 0; // Cột 0
gbc.gridy = 1; // Hàng 1
gbc.weightx = 0.0; // Trở về 0
gbc.fill = GridBagConstraints.NONE;
add(new JLabel("Email liên hệ:"), gbc);
// Cột 2: Ô nhập "Email"
gbc.gridx = 1; // Cột 1
gbc.gridy = 1; // Hàng 1
gbc.weightx = 1.0; // Phình to
gbc.fill = GridBagConstraints.HORIZONTAL;
add(new JTextField(20), gbc);
// ==========================================
// DÒNG 3: NÚT BẤM (Chiếm 2 cột)
// ==========================================
gbc.gridx = 0; // Bắt đầu từ Cột 0
gbc.gridy = 2; // Hàng 2
gbc.gridwidth = 2; // CHIẾM 2 CỘT (Gộp ô ngang)
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.NONE; // Không phình to, giữ nguyên kích thước nút
gbc.anchor = GridBagConstraints.CENTER; // Căn giữa nút trong vùng 2 cột
JButton btnSubmit = new JButton("Đăng ký tài khoản");
btnSubmit.setPreferredSize(new Dimension(200, 35));
add(btnSubmit, gbc);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new GridBagLayoutDemo().setVisible(true));
}
}
Hãy copy chạy thử đoạn code trên và thử dùng chuột kéo rộng cửa sổ ra. Bạn sẽ thấy các nhãn JLabel đứng yên, trong khi JTextField tự động kéo dài ra rất chuyên nghiệp, còn nút bấm vẫn ngoan ngoãn nằm ở chính giữa form.
All rights reserved