Đề Xuất 5/2024 # Lập Trình Giao Diện Người Dùng (Gui Programming) # Top 3 Yêu Thích

Một giao diện người dùng (gọi tắt là GUI) là một cửa sổ (window) chứa các điều khiển (controls) như button, text box, combo box, v.v. Người dùng tương tác với giao diện bằng cách dùng chuột, con trỏ hay bàn phím.

Java cho phép chúng ta tạo giao diện người dùng bằng cách sử dụng một trong hai gói là AWT hay Swing. AWT (Asbtract Window Toolkit) là tập con của Swing nên Swing được dùng chủ yếu nhưng trong chương trình Java phải import cả hai gói Swing và AWT:

import java.awt.*;

import javax.swing.*;

Các điều khiển trong giao diện đồ họa dùng gói Swing luôn bắt đầu bằng chữ “J” ví dụ JLabel, JButton,v.v. Muốn tạo giao diện người dùng, một lớp phải kế thừa lớp JFrame như sau:

import java.awt.*;

import javax.swing.*;

public class MainClass extends JFrame {

public static void main(String[] args)  {

}

}

Đối tượng JFrame là một cửa sổ (window) cho phép chúng ta đóng, thay đổi kích thước và có thể đặt các điều khiển. Một cửa sổ có thể được khởi tạo trong phương thức khởi tạo của một lớp kế thừa lớp JFrame (ví dụ MainClass) như sau:

public MainClass() {

this.setSize(640, 480);

this.setDefaultCloseOperation(EXIT_ON_CLOSE);

this.setVisible(true);

}

Chúng ta dùng phương thức setSize để thiết lập kích thước cho cửa sổ. Phương thức setDefaultCloseOperation cho phép đóng ứng dụng khi cửa sổ đóng, nếu không dùng phương thức này, ứng dụng vẫn tiếp tục chạy khi cửa sổ đã đóng. Phương thức setVisible cho phép hiển thị cửa sổ.

Bây giờ chỉ việc tạo một đối tượng cửa sổ giao diện:

MainClass mainWindow = new MainClass();

Đoạn mã hoàn chỉnh của lớp MainClass:

import java.awt.*;

import javax.swing.*;

public class MainClass extends JFrame {

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

public MainClass() {

this.setSize(640, 480);

this.setDefaultCloseOperation(EXIT_ON_CLOSE);

this.setVisible(true);

}

}

Kết quả khi thực thi:

Các điều khiển không thể đặt trực tiếp đến cửa sổ mà phải được đặt trong đối tượng JPanel. Như vậy, muốn thêm các điều khiển như button, text box, combo box, v.v. đến cửa sổ, đầu tiên chúng ta phải tạo một điều khiển JPanel:

JPanel panel = new JPanel(new FlowLayout());

Đối số cho phương thức khởi tạo của lớp JPanel là một đối tượng FlowLayout. Đây là một đối tượng tạo bố cục (layout) trên đối tượng JPanel. Một vài dạng bố cục phổ biến có thể được mô tả như bảng sau:

Kiểu bố cục Mô tả

BorderLayout Panel được phân chia thành trên (top), dưới (bottom), trái (left), phải (right) và giữa (center)

BoxLayout Đặt các điều khiển trong một cột đơn hay một hàng đơn

CardLayout Cho phép chúng ta chuyển đổi giữa các tập điều khiển

FlowLayout Sắp xếp các điều khiển trên một hàng đơn

GridBagLayout Tạo một bố cục dạng lưới và các điều khiển có thể chiếm giữ nhiều ô

GridLayout Tạo một bố cục dạng lưới

GroupLayout Tạo các bố cục ngang (horizontal) và dọc (vertical) tách biệt

SpringLayout Tạo bố cục cho phép các điều khiển có mối quan hệ theo vị trí của chúng.

Sau khi tạo một đối tượng JPanel với một layout, bước kế tiếp chúng ta sẽ thêm các điều khiển đến đối tượng JPanel này. Đoạn mã sau sẽ thêm điều khiển JLabel và JButton đến đối tượng panel vừa tạo:

panel.add(new JLabel("Test Button: "));

Bước cuối cùng là thiết lập nội dung cho panel với phương thức setContentPanel:

this.setContentPane(panel);

Đoạn mã hoàn chỉnh của lớp MainClass:

import java.awt.*;

import javax.swing.*;

public class MainClass extends JFrame {

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

public MainClass() {

JPanel panel = new JPanel(new FlowLayout());

panel.add(new JLabel("Test Button: "));

this.setContentPane(panel);

this.setSize(640, 480);

this.setDefaultCloseOperation(EXIT_ON_CLOSE);

this.setVisible(true);

}

}

Kết quả:

Các điều khiển là thành phần cơ bản của giao diện đồ họa người dùng và mỗi điều khiển sẽ có những mục đích khác nhau. Java cung cấp nhiều lớp với những phương thức cho những điều khiển riêng biệt và đồng thời cũng cung cấp những phương thức dành cho mọi điều khiển. Ví dụ một button có thể được định dạng qua các phương thức như sau:

JButton btn = new JButton("Initial Text");

btn.setText("New text!");

String text = btn.getText();

btn.setVisible(false);

btn.setVisible(true);

btn.setMargin(new Insets(100, 100, 100, 100));

Dimension dim = btn.getSize();

btn.setBackground(Color.BLUE);

btn.setForeground(Color.WHITE);

btn.setEnabled(false);

btn.setEnabled(true);

btn.setSize(new Dimension(10, 10));

btn.setBounds(new Rectangle(20, 20, 200, 60));

panel.add(btn);

Kết quả:

Sự kiện (events) và xử lý sự kiện (events handling)

Giao diện ActionListener

Sự kiện là các hành động của người dùng như nhấn phím, nhấp chuột, di chuyển chuột, v.v. hay các sự xuất hiện như hệ thống phát sinh một thông báo. Hệ thống cần đáp ứng các sự kiện khi chúng xảy ra, ví dụ hiển thị một thông báo khi người dủng nhấn chuột vào một button.

Để xử lý sự kiện khi chúng xuất hiện, chúng ta cần lắng nghe các sự kiện đó để đưa ra các hành động, ví dụ thực thi đoạn mã hiển thị một thông điệp, phù hợp. Trong Java chúng ta làm điều này bằng cách thực thi các phương thức trong giao diện ActionListener. Ngoài ActionListener, Swing còn cung cấp một danh sách các giao diện lắng nghe các sự kiện có thể tham khảo tại tutorialspoint.com.

Như vậy, một lớp giao diện đồ họa người dùng trong Java có hai đặc điểm cơ bản:

Kế thừa lớp JFrame

Thực thi giao diện ActionListener

Một cách hình dung trực quan như hình sau đây:

Lớp MainClass của chúng ta sẽ thực thi giao diện ActionListener như sau:

import java.awt.*;

import javax.swing.*;

public class MainClass extends JFrame implements ActionListener{

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

public MainClass() {

}

}

Khi chúng ta gõ từ khóa implements ActionListener sẽ có lỗi trong như sau:

Nhấn dòng liên kết đầu tiên để import các lớp phù hợp:

import java.awt.event.ActionListener;

Lúc này lớp MainClass sẽ bị lỗi:

Vẫn nhấn vào dòng liên kết đầu tiên sẽ xuất hiện dòng mã import:

import java.awt.event.ActionEvent;

Và xuất hiện một phương thức actionPerformed trong lớp MainClass:

@Override

public void actionPerformed(ActionEvent e) {

}

Đoạn mã hoàn chỉnh cho lớp MainClass:

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.*;

public class MainClass extends JFrame implements ActionListener{

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

public MainClass() {

}

@Override

public void actionPerformed(ActionEvent e) {

}

}

Bây giờ chúng ta thêm vào cửa sổ giao diện một JLabel và một JButton bằng cách thêm các đoạn mã sau vào phương thức khởi tạo của lớp MainClass:

// Thêm một JLabel

JLabel lbName = new JLabel("Test button: ");

panel.add(lbName);

//Thêm một JButton

Khi người dùng nhấn chuột vào button, một hộp thông điệp sẽ xuất hiện. Đoạn mã xử lý yêu cầu này phải được đặt trong phương thức actionPerformed và sử dụng lớp JOptionPane (xem lại chương III):

@Override

public void actionPerformed(ActionEvent e) {

}

Chúng ta muốn button lắng nghe và thực thi đoạn mã hiển thị hộp thông điệp. Để thực hiện điều này, chúng ta dùng phương thức addActionListener của đối tượng JButton như sau:

//lắng nghe sự kiện từ button

Đoạn mã hoàn chỉnh của lớp MainClass lúc này trông như sau:

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.*;

public class MainClass extends JFrame implements ActionListener{

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

public MainClass() {

JPanel panel = new JPanel(new FlowLayout());

JLabel lbName = new JLabel("Test button: ");

panel.add(lbName);

this.setContentPane(panel);

this.setSize(640, 480);

this.setDefaultCloseOperation(EXIT_ON_CLOSE);

this.setVisible(true);

}

@Override

public void actionPerformed(ActionEvent e) {

}

}

Kết quả:

Xử lý sự kiện cho nhiều điều khiển

Sử dụng lớp nặc danh (anonymous class)

Tạo các lớp thực thi addPerformed cho các yêu cầu khác nhau

Sử dụng lớp nặc danh

Chúng ta có thể thực thi phương thức addPerformed của giao diện ActionListener bằng cách dùng lớp nặc danh. Lúc này, các lớp nặc danh sẽ đóng vai trò tham số cho phương thức addActionListener của các điều khiển. Khi sử dụng lớp nặc danh, chúng ta không cần khai báo implements ActionListener tại lớp MainClass.

Thêm hai JLabel và hai JButton đến giao diện như sau:

public class MainClass extends JFrame {

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

public MainClass() {

JPanel panel = new JPanel(new FlowLayout());

JLabel label1 = new JLabel("Button 1: ");

panel.add(label1);

panel.add(btn1);

JLabel label2 = new JLabel("Button 2: ");

panel.add(label2);

panel.add(btn2);

this.setContentPane(panel);

this.setSize(640, 480);

this.setDefaultCloseOperation(EXIT_ON_CLOSE);

this.setVisible(true);

}

}

Kết quả:

// Thực thi ActionListener dùng lớp nặc danh

btn1.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {

JOptionPane.showMessageDialog(null,"I am button 1");

}

});

// Thực thi ActionListener dùng lớp nặc danh

btn2.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {

JOptionPane.showMessageDialog(null,"I am button 2");

}

});

(Xem mã hoàn chỉnh tại GitHub)

Tạo các lớp thực thi

Vì Java cho phép các lớp lồng nhau nên chúng ta có thể định nghĩa các lớp thực thi phương thức actionPerformed cho từng yêu cầu sự kiện khác nhau của các điều khiển. Lưu ý rằng, với mỗi lớp thực thi phải khai báo thực thi giao diện ActionListener. Định nghĩa hai lớp Button1Handler và Button2Handler trong MainClass:

public class MainClass extends JFrame{

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

public MainClass() {

...

}

private class Button1Handler implements ActionListener{

@Override

public void actionPerformed(ActionEvent e) {

JOptionPane.showMessageDialog(null,"I am button 1");

}

}

private class Button2Handler implements ActionListener{

@Override

public void actionPerformed(ActionEvent e) {

JOptionPane.showMessageDialog(null,"I am button 2");

}

}

}

Và để tiện sử dụng, các biến như label1, label2, btn1,…được khai báo như là biến thành viên của lớp MainClass:

public class MainClass extends JFrame{

private JPanel panel;

private JLabel label1;

private JLabel label2;

private JButton btn1;

private JButton btn2;

public static void main(String[] args)  {

MainClass mainWindow = new MainClass();

}

...

}

Sử dụng các lớp thực thi cho từng button:

btn1.addActionListener(new Button1Handler());

btn2.addActionListener(new Button2Handler());

(Xem đoạn mã hoàn chỉnh tại GitHub)

Tương tác với các điều khiển

private class Button1Handler implements ActionListener{

@Override

public void actionPerformed(ActionEvent e) {

btn1.setText("I am button 1");

}

}

private class Button2Handler implements ActionListener{

@Override

public void actionPerformed(ActionEvent e) {

btn2.setText("I am button 2");

}

}

Khi thực thi chương trình:

Nhấn vào Button 1:

Nhấn vào Button 2:

Tham khảo ví dụ:

Dùng BorderLayout 

Dùng GridLayout