0

Series Java Swing Bài 41: Look and Feel (UIManager) - Thay đổi giao diện toàn cục

Từ Bài 1 đến Bài 40, nếu bạn chạy thử các đoạn code trên máy tính, bạn sẽ nhận ra một điều: Giao diện mặc định của Java Swing trông rất "cổ lỗ sĩ". Các nút bấm vuông vức, thanh cuộn thô kệch, mang đậm phong cách của những năm 2000. Đó là giao diện mặc định có tên là Metal.

Tuy nhiên, sức mạnh thực sự của Swing nằm ở kiến trúc Pluggable Look and Feel (PLAF). Kiến trúc này tách biệt hoàn toàn phần "Logic" (cách nút bấm hoạt động) ra khỏi phần "Giao diện" (cách nút bấm hiển thị). Bằng cách sử dụng UIManager, bạn có thể thay "áo mới" cho toàn bộ phần mềm chỉ với vài dòng code.

1. Look and Feel (LAF) là gì?

Look and Feel bao gồm 2 phần:

  • Look (Nhìn): Hình dáng, màu sắc, font chữ của các Component (Nút bấm bo tròn hay vuông? Nền tối hay sáng?).

  • Feel (Cảm nhận): Cách Component phản hồi khi tương tác (Hiệu ứng khi hover chuột, phím tắt hệ thống).

Java cung cấp sẵn một số LAF tiêu chuẩn:

  1. Cross-platform (Metal): Mặc định, trông giống nhau trên mọi hệ điều hành (Windows, Mac, Linux).

  2. System Native: Bắt chước giống hệt giao diện của hệ điều hành mà phần mềm đang chạy (Ví dụ: Chạy trên Windows 11 sẽ có giao diện Windows 11, chạy trên macOS sẽ có giao diện Mac).

  3. Motif: Giao diện phong cách Unix cổ điển (Rất hiếm dùng hiện nay).

2. Cách áp dụng System Native Look and Feel

Để phần mềm của bạn trông "thuận mắt" và chuyên nghiệp nhất đối với người dùng phổ thông, bạn nên cài đặt System Native LAF.

Quy tắc tối quan trọng: Lệnh thay đổi giao diện phải được gọi trước khi bất kỳ Component nào được khởi tạo (thường đặt ở đầu hàm main).

Java

public static void main(String[] args) {
    try {
        // Lấy tên class giao diện của Hệ điều hành hiện tại và áp dụng
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (Exception e) {
        e.printStackTrace(); // Bỏ qua nếu có lỗi, Swing sẽ tự fallback về Metal
    }

    // Sau khi đổi áo xong mới bắt đầu vẽ Giao diện
    SwingUtilities.invokeLater(() -> new MyApp().setVisible(true));
}

3. Thay đổi Theme lúc đang chạy (Runtime Update)

Nếu bạn muốn tạo tính năng "Đổi Theme" (Chế độ sáng/tối) ngay trong phần mềm mà không bắt người dùng phải khởi động lại, bạn phải làm thêm một bước: Cập nhật lại cây Component.

Sau khi gọi UIManager.setLookAndFeel(...), bạn dùng lệnh:

Java

// Ép toàn bộ các Component bên trong frame phải vẽ lại (Redraw) theo Theme mới
SwingUtilities.updateComponentTreeUI(frame);

4. Kỷ nguyên hiện đại: FlatLaf (Bí kíp của dân chuyên nghiệp)

Mặc dù Native LAF đã tốt hơn Metal, nhưng trong xu hướng phát triển phần mềm hiện đại (đặc biệt khi bạn đã quen với các UI tối giản như TailwindCSS hay giao diện của VS Code), Native LAF đôi khi vẫn chưa đủ đẹp.

Hiện nay, tiêu chuẩn công nghiệp để thiết kế Java Swing là sử dụng một thư viện bên ngoài có tên là FlatLaf (Flat Look and Feel). Đây cũng chính là bộ giao diện mà JetBrains sử dụng cho IntelliJ IDEA và GoLand.

(Lưu ý: Để dùng FlatLaf, bạn cần tải file .jar hoặc cài qua Maven/Gradle. Cú pháp kích hoạt cực kỳ đơn giản):

Java

// Kích hoạt giao diện Tối (Dark Mode) cực kỳ hiện đại
FlatDarkLaf.setup(); 

5. Source Code Thực Chiến: Bảng Điều Khiển Theme Động

Đoạn code dưới đây sẽ lấy ra toàn bộ các giao diện (LAF) đang được cài đặt sẵn trên máy tính của bạn và đưa vào một JComboBox. Khi bạn chọn một Theme khác, toàn bộ phần mềm sẽ "biến hình" ngay lập tức mà không cần khởi động lại.

Java

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

public class LookAndFeelDemo extends JFrame {

    public LookAndFeelDemo() {
        setTitle("Giao diện làm việc của Hiếu - Theme Manager");
        setSize(550, 350);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setLayout(new BorderLayout(10, 10));

        // ==========================================
        // 1. PANEL ĐIỀU KHIỂN THEME (NORTH)
        // ==========================================
        JPanel themePanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        themePanel.add(new JLabel("Chọn Giao diện (Look & Feel): "));

        // Lấy danh sách tất cả các Theme có sẵn trong máy tính
        UIManager.LookAndFeelInfo[] installedLafs = UIManager.getInstalledLookAndFeels();
        JComboBox<String> cbThemes = new JComboBox<>();
        
        for (UIManager.LookAndFeelInfo lafInfo : installedLafs) {
            cbThemes.addItem(lafInfo.getName()); // Thêm tên Theme vào ComboBox
        }
        
        themePanel.add(cbThemes);
        add(themePanel, BorderLayout.NORTH);

        // ==========================================
        // 2. KHU VỰC HIỂN THỊ CÁC COMPONENT ĐỂ TEST UI (CENTER)
        // ==========================================
        // Chúng ta tạo nhiều loại Component khác nhau để thấy sự lột xác của Theme
        JPanel testPanel = new JPanel(new GridLayout(4, 2, 15, 15));
        testPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

        testPanel.add(new JLabel("Họ và Tên:"));
        testPanel.add(new JTextField("Nguyễn Văn A"));

        testPanel.add(new JCheckBox("Kích hoạt tính năng bảo mật"));
        testPanel.add(new JRadioButton("Chế độ ban đêm (Mô phỏng)"));

        testPanel.add(new JLabel("Tiến trình:"));
        JProgressBar progressBar = new JProgressBar(0, 100);
        progressBar.setValue(75);
        progressBar.setStringPainted(true);
        testPanel.add(progressBar);

        testPanel.add(new JButton("Nút Bấm Tiêu Chuẩn"));
        testPanel.add(new JToggleButton("Nút Bấm Trạng Thái"));

        add(testPanel, BorderLayout.CENTER);

        // ==========================================
        // 3. LOGIC XỬ LÝ KHI ĐỔI THEME LÚC ĐANG CHẠY
        // ==========================================
        cbThemes.addActionListener(e -> {
            // Lấy index của Theme được chọn
            int selectedIndex = cbThemes.getSelectedIndex();
            // Lấy ClassName tương ứng của Theme đó
            String className = installedLafs[selectedIndex].getClassName();

            try {
                // 1. Cập nhật hệ thống LAF
                UIManager.setLookAndFeel(className);
                
                // 2. Ép toàn bộ giao diện của Frame này phải vẽ lại (BẮT BUỘC)
                SwingUtilities.updateComponentTreeUI(this);
                
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(this, "Không thể áp dụng Theme này!", "Lỗi", JOptionPane.ERROR_MESSAGE);
            }
        });
    }

    public static void main(String[] args) {
        // Tốt nhất là kích hoạt System Native LAF ngay từ lúc khởi động
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ignored) {}

        SwingUtilities.invokeLater(() -> new LookAndFeelDemo().setVisible(true));
    }
}

Bạn hãy chạy chương trình này. Tại thanh thả xuống (ComboBox) trên cùng, hãy thử chuyển từ Windows (hoặc Mac OS X) sang Metal hoặc Nimbus. Bạn sẽ thấy sự khác biệt một trời một vực về thiết kế của thanh Tiến trình (ProgressBar), các ô CheckBox và viền của Nút bấm.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.