Java Swing Bài 26: MouseListener & MouseMotionListener: Xử lý click, double-click và theo dõi tọa độ chuột.
Ở Bài 25, ActionListener đã làm rất tốt nhiệm vụ bắt sự kiện click cơ bản. Tuy nhiên, nếu bạn muốn làm giao diện thay đổi màu sắc khi người dùng lướt chuột ngang qua (Hover), bắt sự kiện click đúp (Double-click), hay lập trình một ứng dụng vẽ tranh bằng cách kéo thả chuột, ActionListener hoàn toàn "bó tay".
Đó là lúc chúng ta phải nhờ đến sức mạnh của hai bộ lắng nghe: MouseListener (bắt trạng thái click/hover) và MouseMotionListener (bắt sự di chuyển).
1. MouseListener và "Nỗi ám ảnh" 5 phương thức
Interface MouseListener quản lý 5 trạng thái vòng đời của một cú click và hành vi lướt chuột. Nếu bạn implements MouseListener theo cách thông thường, Java sẽ ép bạn phải viết (Override) đủ cả 5 hàm này, dù bạn chỉ cần dùng 1 hàm:
-
mouseEntered: Chuột vừa bước vào ranh giới của Component (Hover In). -
mouseExited: Chuột vừa rời khỏi ranh giới Component (Hover Out). -
mousePressed: Khoảnh khắc nút chuột bị đè xuống. -
mouseReleased: Khoảnh khắc nút chuột được nhả ra. -
mouseClicked: Hoàn thành một chu kỳ Press + Release nhanh gọn tại cùng một vị trí.
Vị cứu tinh: MouseAdapter
Thay vì dùng Interface MouseListener dài dòng, Java cung cấp một class trừu tượng là MouseAdapter đã implement sẵn 5 hàm trên dưới dạng rỗng. Bạn chỉ cần Override đúng cái hàm bạn cần.
Java
// Bắt sự kiện Hover đổi màu nền (Rất hay dùng để làm Custom Button)
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
panel.setBackground(Color.LIGHT_GRAY); // Đổi màu khi chuột vào
}
@Override
public void mouseExited(MouseEvent e) {
panel.setBackground(Color.WHITE); // Trả lại màu cũ khi chuột ra
}
});
2. Bắt sự kiện Double-Click (Click đúp) và Chuột phải
Biến MouseEvent e mang theo tất cả "lý lịch" của cú click chuột. Bạn có thể dễ dàng truy vấn nó:
Bắt Double-Click: Sử dụng e.getClickCount().
Java
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
System.out.println("Bạn vừa click đúp!");
}
}
});
Phân biệt Chuột trái, Chuột phải, Chuột giữa: Sử dụng SwingUtilities.isRightMouseButton(e).
Java
if (SwingUtilities.isLeftMouseButton(e)) {
System.out.println("Click trái");
} else if (SwingUtilities.isRightMouseButton(e)) {
System.out.println("Click phải");
} else if (SwingUtilities.isMiddleMouseButton(e)) {
System.out.println("Click con lăn ở giữa");
}
3. MouseMotionListener: Theo dõi tọa độ và Kéo thả
Nếu bạn cần biết con chuột đang ở tọa độ nào, hoặc người dùng đang kéo thả (Drag) một vật thể, bạn phải gắn thêm MouseMotionListener.
Có 2 phương thức:
-
mouseMoved: Chuột di chuyển bình thường trên Component (không bấm nút nào). -
mouseDragged: Chuột vừa bị đè (Press) vừa di chuyển (Hành động kéo lê).
Lấy tọa độ:
Hàm e.getX() và e.getY() trả về tọa độ của chuột.
Lưu ý quan trọng: Tọa độ này là tọa độ tương đối so với góc trên-trái (0, 0) của chính cái Component đang lắng nghe, chứ KHÔNG PHẢI tọa độ của toàn bộ màn hình máy tính.
Java
panel.addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
System.out.println("Chuột đang ở: X=" + x + ", Y=" + y);
}
});
(Mẹo: Lớp MouseAdapter ở các phiên bản Java mới đã bao gồm luôn cả 2 hàm của MouseMotionListener, nên bạn chỉ cần dùng 1 MouseAdapter duy nhất cho mọi loại sự kiện chuột).
4. Source Code Thực Chiến: Bảng Cảm Ứng (Touchpad) Mini
Đoạn code dưới đây sẽ tạo ra một vùng "Touchpad". Khi bạn rê chuột vào, vùng đó sẽ đổi màu. Bất cứ khi nào bạn di chuyển chuột, tọa độ X, Y sẽ hiển thị liên tục (giống hệ thống tracking). Nếu bạn click hoặc double-click, hệ thống cũng sẽ nhận diện ngay lập tức.
Java
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MouseEventDemo extends JFrame {
private JLabel lblStatus; // Nhãn hiển thị trạng thái ở dưới đáy
public MouseEventDemo() {
setTitle("Làm chủ Mouse Event");
setSize(500, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new BorderLayout());
// ==========================================
// 1. TẠO KHU VỰC TOUCHPAD (JPanel)
// ==========================================
JPanel touchPad = new JPanel();
touchPad.setBackground(new Color(236, 240, 241));
touchPad.setBorder(BorderFactory.createTitledBorder("Khu vực tương tác chuột (Di chuyển, Click, Drag)"));
// Căn giữa chữ bên trong bảng
touchPad.setLayout(new GridBagLayout());
JLabel lblCenter = new JLabel("Rê chuột vào đây");
lblCenter.setFont(new Font("Arial", Font.BOLD, 24));
lblCenter.setForeground(Color.GRAY);
touchPad.add(lblCenter);
// ==========================================
// 2. THANH TRẠNG THÁI (Hiển thị tọa độ)
// ==========================================
lblStatus = new JLabel(" Trạng thái: Sẵn sàng | Tọa độ: (0, 0)");
lblStatus.setFont(new Font("Consolas", Font.PLAIN, 14));
lblStatus.setPreferredSize(new Dimension(500, 30));
lblStatus.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
// ==========================================
// 3. GẮN SỰ KIỆN MOUSE (Gộp chung trong MouseAdapter)
// ==========================================
MouseAdapter mouseHandler = new MouseAdapter() {
// --- CÁC SỰ KIỆN MOUSE LISTENER ---
@Override
public void mouseEntered(MouseEvent e) {
touchPad.setBackground(new Color(253, 235, 208)); // Đổi màu nền sang cam nhạt
lblCenter.setText("Chuột đã vào khu vực!");
}
@Override
public void mouseExited(MouseEvent e) {
touchPad.setBackground(new Color(236, 240, 241)); // Trả lại màu cũ
lblCenter.setText("Rê chuột vào đây");
lblStatus.setText(" Trạng thái: Đứng ngoài vùng | Tọa độ: (?, ?)");
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
lblCenter.setText("DOUBLE CLICK!");
lblCenter.setForeground(Color.RED);
} else if (SwingUtilities.isRightMouseButton(e)) {
lblCenter.setText("Click Chuột Phải");
lblCenter.setForeground(Color.BLUE);
} else {
lblCenter.setText("Click Chuột Trái");
lblCenter.setForeground(Color.BLACK);
}
}
// --- CÁC SỰ KIỆN MOUSE MOTION LISTENER ---
@Override
public void mouseMoved(MouseEvent e) {
// Cập nhật tọa độ liên tục khi di chuyển
updateStatus("Đang di chuyển", e.getX(), e.getY());
}
@Override
public void mouseDragged(MouseEvent e) {
// Xảy ra khi đè chuột và kéo
updateStatus("Đang KÉO THẢ (Drag)", e.getX(), e.getY());
touchPad.setBackground(new Color(212, 239, 223)); // Đổi màu xanh lá
}
};
// Quan trọng: Phải gắn cả 2 loại Listener (dù dùng chung 1 object MouseAdapter)
touchPad.addMouseListener(mouseHandler);
touchPad.addMouseMotionListener(mouseHandler);
// Đưa Component lên giao diện
add(touchPad, BorderLayout.CENTER);
add(lblStatus, BorderLayout.SOUTH);
}
// Hàm tiện ích cập nhật text
private void updateStatus(String action, int x, int y) {
lblStatus.setText(String.format(" Trạng thái: %s | Tọa độ: (%d, %d)", action, x, y));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new MouseEventDemo().setVisible(true));
}
}
All rights reserved