0

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ước →\rightarrow weightx = 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ổ →\rightarrow 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

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í