Series Java Swing Bài 20: JSlider & JProgressBar: Thanh trượt giá trị và thanh hiển thị tiến trình.
Trong thiết kế giao diện, có những con số không nên bắt người dùng phải gõ bằng bàn phím (ví dụ: chỉnh âm lượng, độ sáng màn hình). Tương tự, có những tác vụ cần thời gian xử lý (như tải file, xuất báo cáo) mà nếu để màn hình đứng im, người dùng sẽ tưởng phần mềm bị treo.
Để giải quyết hai vấn đề này, Java Swing cung cấp cặp đôi Component hiển thị theo dạng thanh dài: JSlider (dành cho đầu vào - Input) và JProgressBar (dành cho đầu ra - Output).
1. JSlider: Thanh trượt giá trị (Input)
JSlider cho phép người dùng dùng chuột kéo một "cục bướu" (knob) dọc theo một trục để chọn một giá trị nguyên (int) nằm trong khoảng cho trước.
Khởi tạo và Bật các vạch chia (Ticks & Labels)
Mặc định, JSlider chỉ là một đường thẳng trơn tuột. Để nó trông chuyên nghiệp như một thước đo, bạn cần kích hoạt các vạch chia (Ticks) và nhãn số (Labels).
Java
// Khởi tạo: Hướng ngang, Giá trị Min = 0, Max = 100, Giá trị mặc định lúc mới mở = 50
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 100, 50);
// Thiết lập vạch chia chính (Mỗi 20 đơn vị vẽ một vạch dài)
slider.setMajorTickSpacing(20);
// Thiết lập vạch chia phụ (Mỗi 5 đơn vị vẽ một vạch ngắn)
slider.setMinorTickSpacing(5);
// BẮT BUỘC BẬT 2 hàm này thì vạch và số mới hiện lên
slider.setPaintTicks(true); // Hiển thị vạch
slider.setPaintLabels(true); // Hiển thị số (0, 20, 40, 60, 80, 100)
Bắt sự kiện khi trượt (ChangeListener)
Vì người dùng có thể kéo trượt liên tục, chúng ta không dùng ActionListener (chỉ dùng cho click 1 lần), mà sử dụng ChangeListener. Sự kiện này sẽ bắn ra liên tục mỗi khi giá trị trên thanh trượt thay đổi dù chỉ 1 đơn vị.
Java
slider.addChangeListener(e -> {
int currentVolume = slider.getValue();
System.out.println("Âm lượng hiện tại: " + currentVolume);
});
2. JProgressBar: Thanh tiến trình (Output)
JProgressBar dùng để báo hiệu cho người dùng biết hệ thống đang làm việc và đã hoàn thành được bao nhiêu phần trăm. Nó hoàn toàn "Chỉ đọc" (Read-only), người dùng không thể dùng chuột kéo thanh màu xanh của JProgressBar được.
Hai chế độ hoạt động của JProgressBar
Chế độ 1: Xác định được tiến độ (Determinate) Dùng khi bạn biết chính xác công việc có bao nhiêu bước, hoặc file nặng bao nhiêu MB. Bạn sẽ chủ động dùng code để gọi hàm setValue() tăng dần phần trăm.
Java
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setValue(45); // Đã xong 45%
// Bật tính năng vẽ chữ phần trăm ở chính giữa thanh (Ví dụ: "45%")
progressBar.setStringPainted(true);
Chế độ 2: Vô định (Indeterminate) Dùng khi bạn đang kết nối Database hoặc gọi API mà không biết khi nào mới xong (không biết tổng thời gian). Lúc này, JProgressBar sẽ hiển thị một khối màu chạy qua chạy lại liên tục giống hệt hiệu ứng "Loading".
Java
// Bật chế độ chạy qua chạy lại liên tục
progressBar.setIndeterminate(true);
// Khi API chạy xong, tắt chế độ vô định và set đầy 100%
progressBar.setIndeterminate(false);
progressBar.setValue(100);
3. Source Code Thực Chiến: Trình điều khiển Tải Xuống
Đoạn mã sau kết hợp cả hai Component. Bạn sẽ dùng JSlider để điều chỉnh tốc độ tải, và một JButton để bắt đầu tiến trình tải giả lập hiển thị trên JProgressBar.
(Lưu ý từ Bài 2: Để làm thanh tiến trình chạy mượt mà mà không làm đơ giao diện UI, chúng ta sử dụng javax.swing.Timer thay vì dùng Thread.sleep).
Java
import javax.swing.*;
import java.awt.*;
public class SliderProgressDemo extends JFrame {
private int downloadSpeed = 1; // Tốc độ tải (Lấy từ Slider)
public SliderProgressDemo() {
setTitle("Trình Tải Xuống - Bài 20");
setSize(450, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// Sử dụng GridBagLayout để căn chỉnh form cho đẹp
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(15, 15, 15, 15);
gbc.fill = GridBagConstraints.HORIZONTAL;
// ==========================================
// KHU VỰC 1: JSLIDER (Điều chỉnh tốc độ)
// ==========================================
gbc.gridx = 0; gbc.gridy = 0;
gbc.weightx = 0.0;
add(new JLabel("Tốc độ mạng (MB/s):"), gbc);
gbc.gridx = 1; gbc.gridy = 0;
gbc.weightx = 1.0;
JSlider speedSlider = new JSlider(JSlider.HORIZONTAL, 1, 10, 1);
speedSlider.setMajorTickSpacing(2);
speedSlider.setMinorTickSpacing(1);
speedSlider.setPaintTicks(true);
speedSlider.setPaintLabels(true);
// Bắt sự kiện khi kéo Slider
speedSlider.addChangeListener(e -> {
downloadSpeed = speedSlider.getValue(); // Cập nhật biến tốc độ
});
add(speedSlider, gbc);
// ==========================================
// KHU VỰC 2: JPROGRESSBAR (Thanh tải file)
// ==========================================
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridwidth = 2; // Gộp 2 cột
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setStringPainted(true); // Hiển thị chữ 0%
progressBar.setPreferredSize(new Dimension(300, 30));
progressBar.setForeground(new Color(46, 204, 113)); // Đổi màu xanh lá mạ
add(progressBar, gbc);
// ==========================================
// KHU VỰC 3: NÚT BẤM VÀ LOGIC TIMER
// ==========================================
gbc.gridx = 0; gbc.gridy = 2;
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
JButton btnDownload = new JButton("Bắt đầu tải");
// Tạo một Timer chạy lặp lại mỗi 100ms (0.1 giây)
Timer timer = new Timer(100, e -> {
int currentVal = progressBar.getValue();
if (currentVal >= 100) {
((Timer)e.getSource()).stop(); // Đầy 100% thì dừng Timer
btnDownload.setText("Tải hoàn tất!");
btnDownload.setEnabled(true);
} else {
// Tăng giá trị dựa trên tốc độ từ Slider
int nextVal = Math.min(currentVal + downloadSpeed, 100);
progressBar.setValue(nextVal);
}
});
btnDownload.addActionListener(e -> {
progressBar.setValue(0); // Reset thanh trình về 0
btnDownload.setEnabled(false); // Khóa nút trong lúc đang tải
btnDownload.setText("Đang tải...");
timer.start(); // Bắt đầu chạy Timer
});
add(btnDownload, gbc);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new SliderProgressDemo().setVisible(true));
}
}
All rights reserved