Series Java Swing Bài 28: WindowListener - Xử lý logic khi thu nhỏ, phóng to và Cảnh báo trước khi thoát
Mọi cửa sổ ứng dụng trên hệ điều hành đều có một vòng đời: Mở lên, được người dùng sử dụng (Activate), thu nhỏ xuống thanh Taskbar (Minimize), phóng to trở lại, và cuối cùng là bị đóng (bấm nút X).
Đôi khi, bạn muốn ứng dụng của mình "phản ứng" lại các trạng thái này. Điển hình nhất là tính năng: Bật hộp thoại hỏi "Bạn có chắc chắn muốn thoát không? Dữ liệu chưa lưu sẽ bị mất" khi người dùng vô tình bấm nhầm vào dấu X.
Để làm được điều đó, Swing cung cấp bộ lắng nghe WindowListener.
1. Vòng đời cửa sổ và WindowAdapter
Giống như MouseListener (Bài 26), Interface WindowListener chứa tới 7 phương thức mô tả 7 trạng thái của cửa sổ:
-
windowOpened: Cửa sổ vừa được mở lên lần đầu tiên. -
windowClosing: Người dùng vừa bấm dấu X (Cửa sổ ĐANG TRONG QUÁ TRÌNH đóng, chưa đóng hẳn). -
windowClosed: Cửa sổ ĐÃ ĐÓNG và bị giải phóng khỏi bộ nhớ (dùng hàmdispose()). -
windowIconified: Cửa sổ bị thu nhỏ (Minimize) xuống thanh Taskbar. -
windowDeiconified: Cửa sổ được phóng to/khôi phục lại từ thanh Taskbar. -
windowActivated: Cửa sổ được click chọn, trở thành cửa sổ đang hoạt động (được Focus). -
windowDeactivated: Cửa sổ mất Focus (người dùng click sang một phần mềm khác).
Và tất nhiên, để không phải gánh vác việc viết đè cả 7 phương thức này, chúng ta sẽ sử dụng lớp WindowAdapter và chỉ ghi đè (Override) những trạng thái cần thiết.
2. Bí kíp bắt sự kiện thoát: DO_NOTHING_ON_CLOSE
Nếu bạn chỉ gắn WindowListener vào JFrame và viết code bắt sự kiện windowClosing, bạn sẽ nhận ra một điều tồi tệ: Hộp thoại cảnh báo có hiện lên, nhưng ứng dụng cũng... tắt luôn!
Nguyên nhân: Từ Bài 1 đến giờ, chúng ta luôn dùng câu lệnh: setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Câu lệnh này ra lệnh cho Java: "Cứ thấy người dùng bấm dấu X là lập tức kill (giết) chương trình, không cần hỏi han gì cả".
Cách giải quyết: Để chiếm quyền kiểm soát nút X, bạn phải tước bỏ quyền tự động tắt của JFrame bằng hằng số DO_NOTHING_ON_CLOSE. Sau đó, bạn tự mình hiển thị hộp thoại xác nhận (Bài 24), và tự gọi lệnh System.exit(0) nếu người dùng chọn "Yes".
Java
// 1. Chặn hành vi tự động tắt cửa sổ
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
// 2. Tự xử lý logic khi cửa sổ chuẩn bị đóng
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int confirm = JOptionPane.showConfirmDialog(frame, "Bạn có muốn thoát?", "Xác nhận", JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
System.exit(0); // Lệnh tắt chương trình hoàn toàn
}
}
});
3. Quản lý trạng thái Thu nhỏ / Phóng to
Ngoài việc chặn tắt cửa sổ, WindowListener rất hữu ích khi bạn muốn tối ưu tài nguyên máy tính. Ví dụ: Ứng dụng của bạn có một luồng (Thread) liên tục tải video hoặc render hình ảnh. Khi người dùng thu nhỏ cửa sổ xuống Taskbar (windowIconified), bạn có thể tạm dừng luồng đó để tiết kiệm CPU, và tiếp tục chạy lại khi cửa sổ được khôi phục (windowDeiconified).
Java
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowIconified(WindowEvent e) {
System.out.println("Cửa sổ đã bị thu nhỏ. Tạm dừng các tác vụ nặng...");
}
@Override
public void windowDeiconified(WindowEvent e) {
System.out.println("Cửa sổ đã khôi phục. Tiếp tục xử lý...");
}
});
4. Source Code Thực Chiến: Cửa Sổ Bất Tử
Đoạn code dưới đây tạo ra một Form Notepad. Khi bạn gõ bất kỳ nội dung nào vào khung soạn thảo và bấm nút X, phần mềm sẽ chặn bạn lại để cảnh báo. Đồng thời, nó sẽ thay đổi tiêu đề (Title) khi bạn thu nhỏ cửa sổ.
Java
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class WindowListenerDemo extends JFrame {
private JTextArea textArea;
public WindowListenerDemo() {
setTitle("Notepad Thần Thánh - Bài 28");
setSize(500, 350);
setLocationRelativeTo(null);
// BƯỚC 1 CỰC KỲ QUAN TRỌNG: Ngăn chặn tự động tắt cửa sổ
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
// Tạo giao diện đơn giản
textArea = new JTextArea("Hãy gõ thêm vài chữ vào đây, sau đó bấm nút [X] để thoát...");
textArea.setFont(new Font("Arial", Font.PLAIN, 16));
textArea.setLineWrap(true);
textArea.setMargin(new Insets(10, 10, 10, 10));
add(new JScrollPane(textArea), BorderLayout.CENTER);
// BƯỚC 2: Gắn WindowListener để chiếm quyền kiểm soát
addWindowListener(new WindowAdapter() {
// -----------------------------------------------------
// 1. XỬ LÝ KHI BẤM NÚT [X] (Đang đóng cửa sổ)
// -----------------------------------------------------
@Override
public void windowClosing(WindowEvent e) {
// Kiểm tra xem người dùng có gõ thêm gì không
if (!textArea.getText().isEmpty()) {
// Hiện hộp thoại hỏi
int result = JOptionPane.showConfirmDialog(
WindowListenerDemo.this,
"Bạn có dữ liệu chưa lưu. Bạn có chắc chắn muốn thoát ứng dụng không?",
"Cảnh báo thoát",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE
);
// Nếu chọn Yes (Đồng ý thoát)
if (result == JOptionPane.YES_OPTION) {
System.out.println("Đang dọn dẹp tài nguyên...");
// Thực hiện ngắt kết nối Database ở đây (nếu có)
dispose(); // Giải phóng JFrame khỏi bộ nhớ
System.exit(0); // Tắt tiến trình JVM
}
// Nếu chọn No, cửa sổ vẫn giữ nguyên (DO_NOTHING)
} else {
// Nếu không có chữ nào, tắt luôn không cần hỏi
System.exit(0);
}
}
// -----------------------------------------------------
// 2. XỬ LÝ KHI THU NHỎ CỬA SỔ XUỐNG TASKBAR
// -----------------------------------------------------
@Override
public void windowIconified(WindowEvent e) {
System.out.println("SỰ KIỆN: Cửa sổ đã bị thu nhỏ (Minimized)");
// Đổi tiêu đề ứng dụng
setTitle("Notepad [Đang chạy ngầm]");
}
// -----------------------------------------------------
// 3. XỬ LÝ KHI KHÔI PHỤC CỬA SỔ
// -----------------------------------------------------
@Override
public void windowDeiconified(WindowEvent e) {
System.out.println("SỰ KIỆN: Cửa sổ đã khôi phục (Restored)");
setTitle("Notepad Thần Thánh - Bài 28");
}
});
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new WindowListenerDemo().setVisible(true);
});
}
}
Bạn hãy chạy code, xóa sạch chữ đi và bấm X -> Cửa sổ tắt luôn. Chạy lại, gõ thêm chữ và bấm X -> Hệ thống lập tức chặn lại và hỏi ý kiến bạn. Thử ấn nút Dấu trừ (-) ở góc trên bên phải cửa sổ để xem thông báo in ra trong Console.
All rights reserved