CodeGym /Các khóa học /JAVA 25 SELF /Trình lắng nghe (Listeners) và các interface sự kiện

Trình lắng nghe (Listeners) và các interface sự kiện

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu về sự kiện

Trong lập trình, một sự kiện là tín hiệu cho biết điều gì đó đã xảy ra. Đó có thể là việc nhấn nút, nhập văn bản, hoàn tất tải dữ liệu, thay đổi giá trị biến — bất cứ điều gì có thể xảy ra trong ứng dụng của bạn mà bạn, với tư cách là lập trình viên, muốn phản hồi.

Ví von thì sự kiện giống như tiếng chuông cửa: có ai đó đến và bạn quyết định phải làm gì tiếp theo. Bạn có thể mở cửa, phớt lờ, giả vờ như không có ai ở nhà, hoặc thậm chí gọi mọi người trong nhà ra xem ai đến. Trong lập trình, sự kiện là “tiếng chuông”, còn phản ứng của bạn là handler của sự kiện.

Trong Java, sự kiện là nền tảng của lập trình phản ứng và lập trình giao diện đồ họa (GUI). Không có chúng, sẽ không có các nút bấm, danh sách thả xuống, ứng dụng cửa sổ và thậm chí nhiều hệ thống phía máy chủ.

Listener: là gì và dùng để làm gì

Trình lắng nghe (Listener) là một đối tượng đăng ký theo dõi một sự kiện cụ thể và chờ nó xảy ra. Khi sự kiện diễn ra, listener nhận tín hiệu và thực thi đoạn mã phản ứng đã được chỉ định.

Trong Java, listeners thường được triển khai thông qua các interface. Một ví dụ phổ biến là interface ActionListener. Các lớp triển khai nó sẽ phản hồi hành động của người dùng, chẳng hạn như nhấn nút.

Trong thực tế, tương tác diễn ra như sau: có nguồn phát sự kiện, ví dụ một nút bấm, và có listener — đối tượng triển khai interface phù hợp. Nguồn phát giữ danh sách tất cả listeners đã đăng ký với nó. Khi người dùng nhấn nút, nguồn phát sự kiện sẽ gọi một phương thức đặc biệt trên mỗi listener.

Sơ đồ này giống như việc đăng ký nhận tin: miễn là bạn còn đăng ký, bạn sẽ nhận thông báo. Trong Java, thông báo đó chính là lời gọi tới phương thức của listener, phương thức này thực thi các hành động đã mô tả trước.

2. Các interface sự kiện: ActionListener, MouseListener và những cái khác

Trong Java, mỗi loại sự kiện có một interface listener riêng:

Loại sự kiện Interface listener Nơi sử dụng
Hành động
ActionListener
Nút bấm, menu, bộ hẹn giờ
Chuột
MouseListener
Các thành phần phản ứng với chuột
Bàn phím
KeyListener
Trường văn bản, mọi thành phần
Thay đổi
ChangeListener
Thanh trượt, hộp kiểm, mô hình dữ liệu
Tài liệu
DocumentListener
Thay đổi văn bản (ví dụ: JTextField)

Mỗi interface như vậy định nghĩa một hoặc nhiều phương thức cần triển khai. Ví dụ, ActionListener yêu cầu triển khai phương thức actionPerformed(ActionEvent e).

Ví dụ: ActionListener

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class MyActionListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Nút đã được nhấn!");
    }
}

3. Nguyên lý hoạt động của mô hình sự kiện

Toàn bộ mô hình sự kiện của Java dựa trên một cơ chế đơn giản nhưng mạnh mẽ:

  1. Nguồn phát sự kiện (ví dụ, một nút bấm) duy trì danh sách listeners.
  2. Khi có hành động xảy ra (ví dụ, người dùng nhấn nút), nguồn phát tạo ra một đối tượng sự kiện (ví dụ, ActionEvent).
  3. Nguồn phát duyệt qua tất cả listeners đã đăng ký và gọi phương thức tương ứng của họ (ví dụ, actionPerformed), chuyển vào đối tượng sự kiện.
  4. Mỗi listener tự quyết định sẽ làm gì với sự kiện đó.

Sơ đồ hoạt động của sự kiện:

flowchart LR
    A[Người dùng nhấn nút] --> B[Nút tạo ra sự kiện]
    B --> C[Nút gọi actionPerformed trên tất cả trình lắng nghe]
    C --> D[Trình lắng nghe phản hồi: thực thi mã của mình]

Ví dụ mã:

import javax.swing.*;

public class EventDemo {
    public static void main(String[] args) {
        JButton button = new JButton("Nhấn tôi!");

        button.addActionListener(new MyActionListener());

        JFrame frame = new JFrame("Ví dụ về sự kiện");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(button);
        frame.setSize(200, 100);
        frame.setVisible(true);
    }
}

Khi người dùng nhấn nút, phương thức actionPerformed của tất cả listeners sẽ được kích hoạt.

4. Ví dụ: thêm listener vào nút

Hãy xem sơ đồ kinh điển với một ví dụ đơn giản.

Bước 1. Tạo nút

JButton button = new JButton("Nói xin chào");

Bước 2. Tạo listener

class HelloListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Xin chào, thế giới!");
    }
}

Bước 3. Đăng ký listener

button.addActionListener(new HelloListener());

Ngắn gọn: trông như thế nào trong mã

JButton button = new JButton("Nói xin chào");
button.addActionListener(new HelloListener());

5. Lớp ẩn danh và biểu thức lambda: ngắn gọn và tiện lợi

Lớp ẩn danh

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Trình lắng nghe ẩn danh: xin chào!");
    }
});

Biểu thức lambda (Java 8+)

button.addActionListener(e -> System.out.println("Lambda! Xin chào!"));

Lambda là cách ngắn gọn nhất để thêm listener khi phản ứng của bạn chỉ là một hai dòng.

6. Điều này liên quan gì đến ứng dụng của bạn?

Hãy tích hợp cơ chế này vào ứng dụng học tập của bạn. Ví dụ, ta có một cửa sổ đơn giản với nút “Thêm tác vụ”. Khi nhấn nút, ta muốn thêm một tác vụ mới vào danh sách và in ra thông báo.

Ví dụ mã:

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

public class TaskManagerGUI {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Trình quản lý tác vụ");
        JButton addButton = new JButton("Thêm tác vụ");

        // Sử dụng lambda làm listener
        addButton.addActionListener(e -> {
            System.out.println("Đã thêm tác vụ!");
            // Ở đây có thể là logic thêm tác vụ vào danh sách
        });

        frame.add(addButton);
        frame.setSize(300, 100);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

Bây giờ ứng dụng của bạn không chỉ thực thi tuần tự, mà còn “phản ứng” với hành động của người dùng!

7. Các lỗi thường gặp khi làm việc với listeners và sự kiện

Lỗi số 1: quên đăng ký listener. Nếu bạn đã tạo listener nhưng không thêm nó vào nguồn phát sự kiện, mã của bạn sẽ không bao giờ được gọi. Điều này giống như đăng ký nhận tạp chí nhưng không gửi đơn — tạp chí sẽ không bao giờ tới.

Lỗi số 2: đăng ký cùng một listener nhiều lần. Nếu bạn vô tình thêm cùng một listener nhiều lần, handler sẽ được gọi bấy nhiêu lần. Đôi khi điều này hữu ích, nhưng thường là nguồn gốc của các lỗi khó chịu (“tại sao hàm của tôi chạy ba lần?!”).

Lỗi số 3: quên gỡ bỏ listener. Nếu listener không còn cần thiết nhưng bạn không gỡ bỏ nó, nó vẫn treo trong bộ nhớ. Trong các ứng dụng chạy lâu, điều này có thể dẫn đến rò rỉ bộ nhớ.

Lỗi số 4: thực hiện tác vụ nặng trong handler sự kiện. Nếu bạn làm việc nặng ngay trong handler (ví dụ tải dữ liệu từ Internet), giao diện sẽ “bị đơ” và người dùng sẽ sốt ruột. Tốt hơn là chạy công việc nặng trong một luồng riêng.

Lỗi số 5: ngoại lệ không được xử lý trong listener. Nếu xảy ra ngoại lệ trong handler của bạn, nó có thể “phá vỡ” cả chuỗi sự kiện. Hãy ghi log lỗi và xử lý ngoại lệ để ứng dụng không bị sập.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION