You are on page 1of 69

Giáo trình Java

Phần VI: Tạo ra một GUI với JFC/Swing

TÓM TẮT
Trong phần này sẽ hướng dẫn bạn cách tạo ra những giao diện người dùng đồ hoạ
(GUIs) với JFC/Swing cho những ứng dụng và applets thông qua việc sử dụng
những thành phần Swing. Những thành phần Swing, một bộ phận của JavaTM
Foundation Classes (JFC), có thể được sử dụng hoặc với JDKTM 1.1 hoặc JavaTM 2
platform.
Ghi chú: phần này không hướng dẫn bạn cách sử dụng những thành phần
AWT
1. Nhập môn với Swing
Trong phần này sẽ lướt qua nhanh và nhằm mục đích là cho bạn
hình dung về JFC và Swing. Sau đó sẽ là cách để biên dịch và
chạy chương trình - cho cả ứng dụng và các applets thông qua
việc sử dụng các thành phần Swing bằng một chương trình đơn
giản.

2. Các khái niệm và chức năng của Swing


Cung cấp các thông tin cần thiết để sử dụng các thành phần Swing một cách có
hiệu quả. Ví dụ như cách một chương trình Swing hiển thị giao diện đồ hoạ người
dùng, cách quản lý các sự kiện như kích chuột và cuối cùng sẽ là việc sử dụng các
khái niệm và chức năng như thế nào trong một chương trình thực sự.
3. Sử dụng các thành phần Swing
Hướng dẫn cách sử dụng mỗi thành phần Swing – button, table, các thành phần
text…(nhìn chung các thành phần Swing cung tương tự như các thành phần AWT.
Tuy nhiên, chúng có một số tính năng mới. Ví dụ như button, label có thể nạp hình
ảnh,...)
4. Sử dụng các chức năng khác của Swing.
Nói thêm về các thành phần khác của Swing như actions, borders, icons, và timers.
Ngoài ra sẽ còn hướng dẫn bạn cách tạo một chương trình đa tuyến
(multithreaded)
5. Các thành phần Laying Out là đối tượng chứa.
Hướng dẫn cách chọn một (layout manager), cách sử dụng của mỗi lớp layout
manager mà Java cung cấp.
6. Cách viết một sự kiện Listener
Hướng dẫn cách nắm bắt các sự kiện trong chương trình
7. Làm việc với đồ họa

9095883.doc 1
Giáo trình Java
Sử dụng các đường vẽ, các text để chế tạo ra một hình ảnh, kể cả các hình ảnh
động.
8. Chuyển đổi thành Swing
Hướng dẫn cách chuyển đổi một chương trình sử dụng AWT API 1.1 sang chương
trình sử dụng các thành phần Swing.

Bài 1: Bắt đầu với Swing


1. Đôi điều về JFC và Swing
JFC (Java Foundation Classes) là một tập hợp các chức năng giúp người dùng xây
dựng giao diện đồ hoạ (GUIs). JFC được công bố lần đầu tiên vào năm 1997 trong
hội nghị về những nhà phát triển JavaOne, bao gồm những chức năng sau:
1. Các thành phần Swing
2. Hỗ trợ Pluggable Look and Feel
3. Truy cập API
4. Java 2D API (Java 2 Platform only)
5. Hỗ trợ Drag and Drop (Java 2 Platform only)
3 chức năng đầu của JFC được thực hiện mà không cần bảng mã quốc gia mà dựa vào
các hàm API có trong JDK 1.1
Những phiên bản có Swing API
Swing API đã được sử dụng trong 2 dạng sau:
(1) Là phần cốt lõi của Java 2 Platform (phiên bản chuẩn của v 1.2 va v 1.3)
(2) JFC 1.1 (sử dụng với JDK 1.1)

Với mỗi phiên bản sử dụng, tùy thuộc vào việc chúng ta cần đến JDK 1.1 hay là Java
2 Platform và không cần phải thêm bất kỳ thư viện nào mà vẫn có thể sử dụng được
Swing API. Tuy nhiên, nếu cần sử dụng JDK 1.1 thì thêm vào Swing API (sử dụng
JFC 1.1)
Hãng Sun đã phát hành nhiều phiên bản của JFC 1.1 và để nhận biết nó là phiên bản
nào thì cần dựa vào phiên bản của Swing API chứa trong nó.
Bảng sau đây liệt kê các thông tin về Swing API.

Tương ứng với


Tương ứng với
Swing API phiên bản
phiên bản Chú thích
Version Java 2 Platform
JFC 1.1
(Standard Edition)
Swing 1.0.3 JFC 1.1 Không Phiên bản của JFC 1.1 bao gồm Java Plug-
(with inTM 1.1.1.

9095883.doc 2
Giáo trình Java
Swing 1.0.3)
Phiên bản đầu tiên có đầy đủ Swing 1.1 API
hổ trợ để sử dụng các sản phầm thương mại.
JFC 1.1 Java Plug-in 1.1.2 và Java Plug-in 1.2 cung
Swing 1.1 v 1.2, v 1.2.1
(with Swing 1.1) cấp applet hổ trợ cho từng JDK 1.1 +
Swing 1.1 và Java 2 Platform v 1.2 tương
ứng.
Thêm vào các tính năng mở rộng, xữ lý
nhiều lỗi kỹ thuật, các chức năng mới (API
JFC 1.1
không cần thay đổi) cho Swing 1.1. Java
Swing 1.1.1 (with v 1.2.2
Plug-in 1.1.3 và Java Plug-in 1.2.2 cung
Swing 1.1.1)
cấp applet hổ trợ choJDK 1.1 + Swing 1.1.1
và Java 2 Platform v 1.2.2, tương ứng.
Thêm vào các tính năng, xữ lý nhiều lỗi kỹ
Không có sự thuật, các chức năng mới. Bổ sung thêm các
khác biệt về Không có v 1.3 Beta tính năng và các hàm API mới. Java Plug-in
"Swing" version 1.3 Beta cung cấp applet hổ trợ cho phiên
bản này.

Các gói Swing


Swing API vừa lớn mạnh, lại vừa mềm dẽo. Trong phiên bản 1.1 của API có 15 gói
dùng chung: javax.accessibility, javax.swing, javax.swing.border,
javax.swing.colorchooser, javax.swing.event, javax.swing.filechooser,
javax.swing.plaf, javax.swing.plaf.basic, javax.swing.plaf.metal,
javax.swing.plaf.multi, javax.swing.table, javax.swing.text,
javax.swing.text.html, javax.swing.tree, and javax.swing.undo.
Trong hầu hết các chương trình chỉ sử dụng một phần nhỏ của API, chủ yếu là các gói
Swing javax.swing, javax.swing.event.
Sự khác biêt giữa các thành phần Swing và AWT
Các thành phần AWT được cung cấp trong JDK 1.0 và 1.1. Mặc dù Java 2 Platform
vẫn còn hỗ trợ các thành phần AWT, tuy nhiên, chúng ta nên dùng các thành phần
Swing. Khi sử dụng, có thể phân biệt các thành phần Swing với các thành phần AWT.
Các thành phần Swing có tên bắt đầu bằng ký tự J. Ví dụ như, lớp AWT button class
có tên là Button thì lớp Swing button có tên là Jbutton. Ngoài ra, các thành phần AWT
thì nằm trong gói java.awt, còn các thành phần Swing thì nằm trong gói javax.swing.
Do đó, khi sử dụng các thành phần Swing thì trong phần khai báo của chương trình, ta
nhớ thêm vào dòng lệnh sau:
import javax.swing.*;
Sự khác biệt lớn nhất giữa các thành phần AWT và Swing đấy là các thành phần
Swing được thực thi mà hoàn toàn không cần mã nguồn (with absolutely no native
code). Kể từ khi các thành phần Swing không còn bị hạn chế trong những khuôn mẫu
thông thường, (the least common denominator), tức là lúc mà các tính năng của nó đã
có mặt hầu hết trong các phiên bản, thì các chức năng của nó đã mở rộng hơn nhiều so
với các chức năng của các thành phần AWT. Do các thành phần Swing không có mã

9095883.doc 3
Giáo trình Java
nguồn (native code), nên chúng có thể được thêm vào như là một add-on của JDK 1.1
hay như một phần của Java 2 Platform.
Ngay cả những thành phần Swing đơn giản nhất cũng đã có những khả năng vượt xa
các thành phần AWT:
• Swing buttons và labels có thể hiển thị hình ảnh và cả văn bản.
• Có thể dễ dàng thay đổi đường viền của hầu hết các thành phần Swing. Ta có thể
dễ dàng thay đổi đường viền của một label hay một đối tượng chứa nào đó.
• Có thể dễ dàng thay đổi hành vi hay giao diện của một thành phần Swing trong
phương thức điều khiển của nó hoặc tạo ra một lớp con (subclass) của chính
thành phần đó.
• Thành phần Swing không có hình chữ nhật. Do đó, các nút lệnh có thể có hình
tròn hoặc bo góc.
• Kỹ thuật hỗ trợ (Assistive technologies) qua việc đọc màn hình có thể dể dàng
lấy thông tin từ các thành phần Swing.
2. Biên dịch và thực thi một chương trình Swing
Ðể viết một chương trình sử dụng các thành phần Swing, trước tiên, ta phải có phiên
bản của JDK và JFC tương ứng. Biên dịch và thực thi một chương trình Swing còn
tùy thuộc vào việc đang sử dụng JDK 1.1 hay Java 2 Platform. Nếu sử dụng Java 2
Platform thì sẽ đơn giản hơn vì Swing đã được tích hợp.
2.1 Biên dịch và thực thi chương trình với Java 2 Platform, v 1.2 or 1.3
Trong phần này, chúng ta sẽ khảo sát các vấn đề qua ví dụ SwingApplication.java
có giao diện như sau:

Sau đây là các bước trình tự cho việc biên dịch và thực thi một chương trình Swing
với Java 2 SDK, v 1.2 hay v 1.3:
1. Nếu chưa có, cài đặt vào máy phiên bản Java 2 Platform.
2. Tạo một chương trình sử dụng các thành phần Swing.
3. Biên dịch chương trình.
4. Cho thực thi chương trình.

9095883.doc 4
Giáo trình Java
Hiện nay, đang có 2 phiên bản của Java 2 Platform, tất cả đều miễn phí do Sun cung
cấp (bạn có thể vào địa chỉ này để được hổ trợ: http://www.sun.com). Phiên bản thứ
nhất là v 1.2 (Java 2 SDK, Standard Edition v 1.2) và phiên bản thứ hai là v 1.3 (Java
2 SDK, Standard Edition v 1.3).
Ðể tạo một chương trình sử dụng các thành phần Swing, có thể sử dụng chương trình
mẫu mà chúng tôi cung cấp sau đây SwingApplication.java. Cần lưu ý là tên file phải
chính xác là: "SwingApplication.java"
import javax.swing.*; //This is the final package name.
//import com.sun.java.swing.*; //Used by JDK 1.2 Beta 4 and all
//Swing releases before Swing 1.1 Beta 3.
import java.awt.*;
import java.awt.event.*;

public class SwingApplication {


private static String labelPrefix = "Number of button clicks: ";
private int numClicks = 0;

public Component createComponents() {


final JLabel label = new JLabel(labelPrefix + "0 ");

JButton button = new JButton("I'm a Swing button!");


button.setMnemonic(KeyEvent.VK_I);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
numClicks++;
label.setText(labelPrefix + numClicks);
}
});
label.setLabelFor(button);

/*
* An easy way to put space between a top-level container
* and its contents is to put the contents in a JPanel
* that has an "empty" border.
*/
JPanel pane = new JPanel();
pane.setBorder(BorderFactory.createEmptyBorder(
30, //top
30, //left
10, //bottom
30) //right
);
pane.setLayout(new GridLayout(0, 1));
pane.add(button);

9095883.doc 5
Giáo trình Java
pane.add(label);

return pane;
}

public static void main(String[] args) {


try {
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception e) { }

//Create the top-level container and add contents to it.


JFrame frame = new JFrame("SwingApplication");
SwingApplication app = new SwingApplication();
Component contents = app.createComponents();
frame.getContentPane().add(contents, BorderLayout.CENTER);

//Finish setting up the frame, and show it.


frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
}
Bước tiếp theo là biên dịch. Việc biên dịch trở nên đơn giản và dể dàng với Java 2
SDKs khi các gói Swing được tích hợp trong phiên bản Standard Edition của Java 2
Platform. Câu lệnh như sau:
javac -deprecation SwingApplication.java
Trường hợp khi biên dịch không thành công, có thể là do bạn đang sử dụng trình biên
dịch JDK 1.1 thay vì v 1.2 hoặc v 1.3 hoặc đang sử dụng bản beta của Java 2
Platform.
Thực thi chương trình sẽ được thực hiện khi đã biên dịch thành công. Cần đảm bảo là
trong class path đã chỉ đường dẫn đến file thực thi:
Vd: java -classpath .;C:\java\lnfdir\newlnf.jar SwingApplication
Lưu ý: không cần phải gỏ phần đuôi mở rộng .cls. trình thông dịch của phiên bản 1.2
hay 1.3 sẽ tự động dò tìm.
2.2 Biên dịch và thực thi chương trình với JDK 1.1

9095883.doc 6
Giáo trình Java
Cũng tương tự như với Java 2 Platform, nhưng trình biên dịch sử dụng ở đây là JDK
1.1 và JFC 1.1.
3. Thực thi các Swing Applets
Để viết một Swing Applets, trước tiên là phải thực thi được chúng. Phần này sẽ giới
thiệu 2 applets.
Sau đây là phần mã của applets HelloSwingApplet.java
import javax.swing.*; //This is the final package name.
//import com.sun.java.swing.*; //Used by JDK 1.2 Beta 4 and all
//Swing releases before Swing 1.1 Beta 3.
import java.awt.*;

public class HelloSwingApplet extends JApplet {

// This is a hack to avoid an ugly error message in 1.1.


public HelloSwingApplet() {
getRootPane().putClientProperty("defeatSystemEventQueueCh
eck",
Boolean.TRUE);
}

public void init() {


JLabel label = new JLabel(
"You are successfully running a Swing applet!");
label.setHorizontalAlignment(JLabel.CENTER);

//Add border. Should use createLineBorder, but then the


bottom
//and left lines don't appear -- seems to be an off-by-one error.
label.setBorder(BorderFactory.createMatteBorder(1,1,2,2,Color.
black));

getContentPane().add(label, BorderLayout.CENTER);
}
}
Để thực thi một Swing Applets, theo trình tự các bước sau:
(1) Đảm bảo là đã có trình duyệt 1.1 hoặc 1.2 hoặc tải về Java Plug-in và chúng
là những phiên bản mới nhất. Trường hợp không có các thứ trên, có thể sử
dụng Applet Viewer (appletviewer).
(2) Nếu bạn đang sử dụng trình duyệt 1.1 không có Java Plug-in thì cần phải
nạp file Swing JAR vào trình duyệt.

9095883.doc 7
Giáo trình Java
(3) Kiểm tra lại xem trình duyệt của bạn đã hoàn chỉnh chưa. Trong phạm vi
bài này thì hình dưới đây kiểm chứng cho trình duyệt của bạn là đã đáp ứng
hoàn hay hay chưa. Nếu trong trình duyệt của bạn xuất hiện applet như
dưới đây thì OK. Trường hợp chỉ thấy một thay vì hai applet hoặc không
thấy các hình ảnh thì trình duyệt của bạn chưa thực sự sẵn sàng.

4. Trao đổi thêm về một ứng dụng Swing


Trong phần này, chúng ta sẽ tìm hiểu thêm thông qua chương trình
SwingApplication. SwingApplication sẽ hiển thị cửa sổ như sau:

Phần mã của SwingApplication.java thực hiện những công việc sau:
Importing Swing packages
Dòng lệnh sau sẽ import gói Swing chính:
import javax.swing.*;

Lưu ý: JFC 1.1 và Java 2 SDK v 1.2 phiên bản beta sử dụng tên gọi
khác nhau cho gói Swing.

Hầu hết các chương trình đều cần import hai gói chính của AWT là:
import java.awt.*;
import java.awt.event.*;
Choosing the look and feel
Swing cho phép bạn chỉ định rõ “look and feel” mà chương trình của bạn sử
dụng -- Java look and feel, Windows look and feel, CDE/Motif look and feel,
... Đoạn mã in đậm dưới đây sẽ chỉ ra cho bạn cách mà SwingApplication
chỉ định “look and feel”:
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelCl
assName());

9095883.doc 8
Giáo trình Java
} catch (Exception e) { }

...//Create and show the GUI...


}
Xác lập đối tượng chứa mức đỉnh
Mỗi chương trình được trình diễn đều có giao diện với mức đơn giản nhất là
một đối tượng chứa thuộc thành phần của Swing ở mức đỉnh. Ở hầu hết các
ứng dụng, đối tượng chứa Swing ở mức đỉnh có thể là JFrame, JDialog, hoặc
(cho applets) JApplet. Mỗi đối tượng JFrame sẽ thực hiện một cửa sổ chính
và mỗi JDialog sẽ thực thi cho cửa sổ thứ hai. Mỗi đối tượng JApplet thực
hiện việc hiển thị của một applet bên trong một cửa sổ. Các đối tượng chứa
mức đỉnh của Swing cung cấp các hổ trợ cần thiết để các thành phần Swing
thực hiện việc vẽ và quản lý các hành vi.
Ví dụ SwingApplication chỉ có đối tượng chứa mức đỉnh là
JFrame. Khi người sử dụng đóng frame thi ứng dụng sẽ thoát.
Đoạn mã sau đây xác lập và cho hiển thị frame:
public class SwingApplication {
...
public static void main(String[] args) {
...
JFrame frame = new JFrame("SwingApplication");
//...create the components to go into the frame...
//...stick them in a container named contents...
frame.getContentPane().add(contents,
BorderLayout.CENTER);

//Finish setting up the frame, and show it.


frame.addWindowListener(...);
frame.pack();
frame.setVisible(true);
}
}
Xác lập buttons và labels
Giống như GUIs, giao diện của SwingApplication chứa một button và một
label. Đoạn mã sau đây khởi tạo button đó:
(1) JButton button = new JButton("I'm a Swing button!");
(2) button.setMnemonic(KeyEvent.VK_I);
(3) button.addActionListener(...create an action
listener...);
Dòng lệnh (1) để tạo một nút lệnh với tiêu đề "I'm a Swing button!". Dòng (2)
xác lập key I đóng vai trò tương tự như sự kiện người dùng kích vào nút

9095883.doc 9
Giáo trình Java
lệnh(tức là khi người dùng nhấn Alt-i thì cùng tương tự như kích chuột vào nút
lệnh). Dòng (3) đăng ký việc nắm bắt sự kiện cho việc kích nút lệnh
Đoạn mã sau đây khởi tạo và vận hành hoạt động của label:
..//where instance variables are declared:
private static String labelPrefix = "Number of button
clicks: ";
private int numClicks = 0;
...//in GUI initialization code:
final JLabel label = new JLabel(labelPrefix + "0 ");
...
label.setLabelFor(button);
...//in the event handler for button clicks:
label.setText(labelPrefix + numClicks);
Đoạn mã trên đơn giản, ngoại trừ dòng khởi động phương thức setLabelFor().
Nó xuất hiện ở đây giống như sự gợi ý về kỷ thuật hổ trợ trực tiếp (assistive
technologies) mà thông qua đó, một label được mô tả như là một button.
Thêm thành phần vào các đối tượng chứa.
SwingApplication nhóm label và button vào trong một đối tượng chứa
(JPanel) trước khi thêm vào frame một thành phần khác. Đoạn mã sau đây
khởi tạo một panel:
(1) JPanel pane = new JPanel();
(2) pane.setBorder(BorderFactory.createEmptyBorder(30,
30, 10, 30));
(3) pane.setLayout(new GridLayout(0, 1));
(4) pane.add(button);
(5) pane.add(label);
Dòng (1) dùng để tạo một panel, khai báo một đối tượng có kiểu Jpanel với tên
gọi là pane.
Dòng (2) để tạo đường viền bao bọc cho panel đó.
Dòng (3) dùng đẻ tạo một đối tượng quản lý layout để giám sát tất cả các thành
phần có trong panel sẽ được hiển thị trên một cột.
Dòng (4) và (5) dùng để đưa button và label vào trong panel. Việc thêm button
và label vào trong panel có nghĩa là chúng sẽ được điều khiển bởi layout quản
lý panel đó.
Thêm đường viền (borders) xung quanh một thành phần.
Đoạn mã sau đây sẽ tạo một đường viền cho một panel:
pane.setBorder(BorderFactory.createEmptyBorder(
30, //top

9095883.doc 10
Giáo trình Java
30, //left
10, //bottom
30) //right
);
đường viền này đơn giản chỉ cung cấp một vùng trống của panel. Thêm 30
pixels cho top, left, và right, và 10 pixels cho bottom. Borders là tính năng mà
JPanel thừa kế từ lớp JComponent.
Handling events
SwingApplication chứa 2 event handlers. Một nắm bắt sự kiện kích vào nút
lệnh(action events). Cái kia để nắm bắt sự kiện đóng cửa sổ (window events).
Sau đây là đoạn mã quản lý các sự kiện của SwingApplication:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
numClicks++;
label.setText(labelPrefix + numClicks);
}
});
...
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Dealing with thread issues
Chương trình SwingApplication là một tiến trình an toàn. Bởi vì, một khi
giao diện của nó đang được hiển thị (visible), thì thao tác trên giao diện của nó
chỉ xảy ra cho một event handler. Không thể có hai tiến trình cùng truy xuất
đến một GUI trong cung fmột thời điểm.
Supporting assistive technologies
Hỗ trợ assistive technologies, một thiết bị giống như để đọc màn hình, cung
cấp cách thức để xử lý thông tin trên GUI. Hỗ trợ này đã có trong hầu hết các
thành phần Swing. Trong SwingApplication có một chỗ đề cập đến kỹ thuật
này:
label.setLabelFor(button);
Như đã nói, việc lấy thông tin từ các thành phần Swing là điều đang được quan
tâm, kỹ thuật trên đã giúp cho công việc này được thực hiện một cách dễ dàng:
JButton button = new JButton("I'm a Swing button!");
label = new JLabel(labelPrefix + "0 ");
label.setText(labelPrefix + numClicks);
JFrame frame = new JFrame("SwingApplication");
9095883.doc 11
Giáo trình Java

Bài 2: Các khái niệm và chức năng của Swing


Trong bài này sẽ giới thiệu các đặc trưng của Swing và giảng giải các khái
niệm cần thiết để bạn có thể nắm bắt và sử dụng các thành phần Swing một
cách có hiệu quả. Phần cuối của bài học này sẽ phân tích một chương trình
Swing và đây sẽ là phần tổng kết lại những gì bạn đã học trong bài này.
1. Các thành phần Swing và sự phân cấp giới hạn.
Swing cung cấp rất nhiều thành phần GUI chuẩn như: buttons, lists, menus, và
text areas, là những thành phần mà bạn sẽ phối hợp để tạo nên GUI cho
chương trình của bạn. Ngoài ra, còn có các đối tượng chứa như windows và
tool bars.
Trong phần này , chúng ta sẽ tiếp tục xem xét thông qua ví dụ
SwingApplication đã được mô tả trong phần A Quick Tour of a Swing
Application's.

Ví dụ này sẽ đề cập đến một vài thành phần Swing thường dùng và cách thức
chúng tương tác với nhau trong một GUI và của sự phân cấp giới hạn.
SwingApplication tạo 4 thành phần Swing thường dùng như
sau:
• một frame, hoặc một cửa sổ làm việc chính (JFrame)
• một panel, thông thường gọi là pane (JPanel)
• một button (JButton)
• một label (JLabel)
frame là đối tượng chứa ở mức đỉnh. is a top-level container. Sự hiện
diện của frame nhằm cung cấp một vùng để các thành phần khác thiết lập sự có
mặt của mình trên vùng đó. Ngoài ra còn có các thành phần khác thường được
sử dụng để làm đối tượng chứa mức đỉnh là dialogs (JDialog) và applets
(JApplet).
panel là đối tượng chứa mức trung gian (intermediate container). panel nhằm
mục đích xác định vị trí của button và label. Những đối tượng chứa mức trung
gian khác còn có scroll panes (JScrollPane) và tabbed panes (JTabbedPane),
chúng có ảnh hưởng lẫn nhau, tương tác với nhau rtong giao diện của một
chương trình.

9095883.doc 12
Giáo trình Java
button và label là những thành phần cơ bản (atomic components), những thành
phần mà không thể chứa các thành phần Swing khác AWT thông thường, các
thành phần cơ bản này sẽ là nơi để nhận thông tin đầu vào từ phía người dùng.
Swing API cung cấp nhiều thành phần cơ bản, bao gồm combo boxes
(JComboBox), text fields (JTextField), và tables (JTable).
Hình dưới đây là sơ đồ phân cấp giới hạn của các thành phần trong ví dụ
SwingApplication.

Như hình vẽ trên, ngay cả các chương trình Swing đơn giảnn nhất cũng có
nhiều mức khác nhau. Nhưng bao giờ gốc của sơ đồ vẫn là đối tượng chứa mức
đỉnh, nới để các thành phần Swing khác thể hiện sự tồn tại của mình.

Mách nước: Để xem sự phân cấp của bất kỳ frame hay dialog nào, chỉ
cần kích chuột vào border của nó để chọn, nhấn Control-Shift-F1.

Mỗi một đối tượng chứa mức đỉnh đều gián tiếp chứa một đối tượng chứa
trung gian thường được gọi là content pane. Khi làm việc, bạn không cần quan
tâm thế nào là đối tượng chứa mức đỉnh và cái nào là đối tượng chứa trung
gian. Chương trình sẽ tự động quản lý cho bạn.
pane contains, trực tiếp hoặc gián tiếp chứa tất cả các thành phần sẽ hiển thị
trong GUI. Riêng đối với top-level container thì có menu bar, menu bar sẽ
đứng trong một vùng đặc biệt nằm ngoài content pane.
Để thêm một thành phần vào đối tượng chứa, có thể dùng nhiều cách khác
nhau của phương thức add(). Phương thức add() có ít nhất 1 đối số (argument)
Đoạn mã sau đây sẽ thực hiện việc thêm một button và một label vào trong
panel:
frame = new JFrame(...);
button = new JButton(...);
label = new JLabel(...);
pane = new JPanel();
pane.add(button);
pane.add(label);
frame.getContentPane().add(pane,
BorderLayout.CENTER);
2. Layout Management

9095883.doc 13
Giáo trình Java
Các đối tượng chứa sử dụng layout managers để xác lập kích thước và vị trí
của các thành phần chứa trong nó. Borders sẽ ảnh hưởng đến layout của Swing
GUIs bằng cách làm cho các thành phần lớn lên.
Hình dưới đây hiển thị GUI của 5 chương trình. GUI của chúng khác nhau là
do sử dụng các layout managers khác nhau để xác định kích thước và vị trí của
buttons.

Layout management là quá trình xác định kích thước và vị trí của các thành
phần. Mặc định, mỗi đối tượng chứa sẽ có một layout manager.
Java platform hỗ trợ sử dụng 5 layout managers thông thường nhất:
BorderLayout, BoxLayout, FlowLayout, GridBagLayout, và GridLayout.
Những layout managers được thiết kế để hiển thị đa thành phần trong cùng
một thời điểm. Và lớp thứ 6, CardLayout, là một trường hợp đặc biệt. Nó được
sử dụng để kết hợp các layout managers với nhau.
Xác lập Layout Manager
Bạn có thể dể dàng thay đổi một layout managers trở thành một container để
sử dụng. Đơn giản là chỉ cần gọi phương thức setLayout. Đoạn mã sau đây
sử dụng BorderLayout:
JPanel pane = new JPanel();
pane.setLayout(new BorderLayout());
Một vài gợi ý về Component (Providing Hints about a
Component)
Ta có thể sử dụng các phương thức sau để tùy biến kích thước và vị trí của các
thành phần: setMinimumSize, setPreferredSize, và
setMaximumSize, hoặc có thể xây dựng các lớp con của các thành phần để
khai thác các phương thức như: getMinimumSize, getPreferredSize, và
getMaximumSize.

9095883.doc 14
Giáo trình Java
Bên cạnh việc cung cấp các tùy biến về kích thước, ta cũng còn có thể cung
cấp thêm các tùy biến về việc canh chỉnh, gồm các phương thức sau:
setAlignmentX và setAlignmentY, getAlignmentX và
getAlignmentY methods.
How Layout Management Occurs
Ví dụ sau đây mô tả trình tự quá trình hiển thị của một frame (JFrame).
1. Khi GUI được xây dựng, JFrame sẽ gọi phương thức pack. Việc chỉ
định này sẽ bảo đảm frame xuất hiện đúng với kích thước mà nó đã
được xác lập trước đó.
2. To find the frame's preferred size, the frame's layout manager adds the
size of the frame's edges to the preferred size of the component directly
contained by the frame. This is the sum of the preferred size of the
frame's content pane, plus the size of the frame's menu bar, if any.
3. The content pane's layout manager is responsible for figuring out the
content pane's preferred size. By default, this layout manager is a
BorderLayout object. However, let's assume that we replace it with a
GridLayout object that's set up to create two columns, as in the
bottom right of the preceding snapshot. The interesting thing about grid
layout is that it forces all components to be the same size, and it tries to
make them as wide as the widest component's preferred width and as
high as highest one's preferred height.
First, the grid layout manager queries the content pane
for its insets -- the size of the content pane's border, if
any. Next, the grid layout manager queries each
component in the content pane for its preferred size,
noting the largest preferred width and largest preferred
height. Then it calculates the content pane's preferred
size.
4. When each button is asked for its preferred size, the button first checks
whether the user specified a preferred size. If so, it reports that size. If
not, it queries its look and feel for the preferred size.
3. Event Handling
Event handling thể hiện việc chương trình phản hồi các yêu cầu từ phía bên
ngoài, ví dụ như việc người dùng nhấn phím chuột. Chương trình Swing sẽ
thực hiện tất cả các thao tác và nắm bắt các sự kiện (event handling) bằng cách
thực hiện tiến trình của sự kiện.
Mỗi khi người dùng nhấn phím hay kích chuột, thì một sự kiện xảy ra. Bất kể
đối tượng nào cùng đều được gán bởi một sự kiện. Các thành phần Swing có

9095883.doc 15
Giáo trình Java
thể tạo ra nhiều kiểu sự kiện khác nhau. Bảng sau đây liệt kê một vài kiểu sự
kiện:
Hành động Kiểu Listener
User kích vào nút lệnh, nhấn phím Spacebar khi đang
làm việc trong text field, hoặc kích chọn vào menu ActionListener
item
WindowListene
User đóng một frame (main window)
r
User nhấn một nút chuột trong khi đang rê chuột trên
MouseListener
một thành phần
MouseMotionLi
User di chuyển chuột trên một thành phần
stener
ComponentList
Thành phần hiển thị
ener
Thành phần lấy trạng thái của keyboard FocusListener
ListSelectionLi
Việc chọn lựa trong Table hoặc list có thay đổi
stener
Mỗi sự kiện đều được đại diện bởi một đối tượng và đối tượng đó cung cấp
thông tin về sự kiện cũng như nhận dạng được nơi phat ra sự kiện. Nguồn của
sự kiện thông thường là các thành phần, nhưng những kiểu đối tượng khác
cũng có thể là nguồn của sự kiện. Hình sau đây minh hoạ cho vấn đề này.

Caption: Multiple listeners


can register to be notified of events
of a particular type from a particular source.
Cách thực thi một Event Handler (How to Implement an Event
Handler)
Mỗi event handler đòi hỏi có 3 bước như sau:
1. Trong phần khai báo cho lớp event handler, xác định rõ mỗi lớp sẽ thực
thi một listener interface hoặc kế thừa một lớp mà lớp đó thực thi một
listener interface. Ví dụ:
2. public class MyClass implements ActionListener {

3. Đăng ký sự hiện diện của lớp event handler như là một listener trên một
hoặc nhiều thành phần. Ví dụ:
4. someComponent.addActionListener(instanceOfMyCl
ass);

9095883.doc 16
Giáo trình Java
5. Thực thi những phương thức trong listener interface. Ví dụ:
6. public void actionPerformed(ActionEvent e) {
7. ...//code that reacts to the action...
8. }

Hãy xem xét cách thức một nút lệnh (JButton) nắm bắt sự kiện kích chuột. Để
xác định khi nào thì người dùng kích chuột lên nút lệnh (hoặc dùng các phím
nóng) thì một chương trình phải có đối tượng thực thi giao diện ActionListener.
Chương trình phải đăng ký đối tượng đó như là một action listener trên nút
lệnh (nguồn của sự kiện) bằng cách sử dụng phương thức addActionListener.
Khi user kích lên nút lệnh, nút lệnh sẽ phát ra một hành vi của sự kiện. Đây là
yêu cầu của phương thức actionPerformed. Trong phương thức này, tham số sẽ
là một đối tượng ActionEvent và tham số này sẽ cung cấp thông tin về sự kiện
và nguồn của sự kiện.

Caption: Khi user vào nút lệnh, action listeners của


nút lệnh được phát ra.
Tiến trình và Event Handling (Threads and Event Handling)
Mã của event-handling xảy ra trong một tiến trình đơn hay còn gọi là event-
dispatching thread. Điều này nhằm đảm bảo là mỗi event handler hoàn thành
việc thực hiện trước khi một event handler xảy ra. Ở ví dụ minh hoạ trên,
phương thức actionPerformed được xữ lỹ trong một tiến trình đơn.
4. Painting
Painting nghĩa là vẽ các thành phần trên màn hình. Mặc dầu việc tùy chọn các
thành phần được thực hiện một cách dễ dàng, nhưng hầu hết các chương trình
đều bị làm phức tạp lên bằng cách tùy chọn đường viền cho các thành phần.
Cách làm việc của Painting (How Painting Works)
Khi một Swing GUI cần vẽ lại giao diện của chính nó, hoặc khi cần làm tươi
những điều chỉnh về trạng thái của chương trình, nó sẽ khởi động thành phần ở
mức cao nhất (top-level component) cần được vẽ lại và làm việc dần xuống
theo luồng phân cấp. Quá trình xữ lý này được thực hiện bởi hệ thống AWT
nhàmư làm cho chương trình có vẽ hiệu quả và thích ứng hơn.
Các thành phần Swing có thể vẽ lại chính nó bất kể khi nào cần thiết. Khi gọi
phương thức setText trên một thành phần, thành phần đó sẽ tự động vẽ lại chính
nó, thay đổi kích thước,…

9095883.doc 17
Giáo trình Java
Cũng giống như event-handling, painting cũng được thực hiện trong một tiến
trình đơn. Trong khi một sự kiện đang xảy ra thì quá trình vẽ lại không được
thực hiện.
Quá trình vẽ lại các thành phần sẽ không bị ngắt quảng bởi các sự kiện khác.
Ví dụ minh hoạ về Painting (An Example of Painting)
Để minh hoạ cho quá trình painting, ta sử dụng lại chương trình
SwingApplication. Hình dưới là giao diện của SwingApplication:

Sơ đồ phân cấp:

Khi GUI của SwingApplication được vẽ, quá trình đó xảy ra như sau:
1. Đối tượng chứa mức đỉnh, JFrame, sẽ vễ lại nó trước tiên.

2. Các đối tượng chứa trung gian, trước hết là vẽ lại background, chứa nó.
Sau đó sẽ là JPane.
3. JPanel trước hết sẽ vẽ lại background, sau đó là đường viền và sau
cùng là các thành phần con chứa trong nó.
4. Để vẽ lại, JButton vẽ nền của nó, sau đó là dòng văn bản của chính nó.

5. JLabel vẽ lại văn bản của nó.


Điều này có nghĩa là, các thành phần sẽ vẽ lại chính bản thân nó trước khi nó
điều khiển các thành phần chứa trong nó vẽ lại. Hình sau đây minh hoạ những
thành phần thừa kế từ JComponent và vẽ lại chính bản thân nó:
1. background 2. custom 3. border 4. children
(if opaque) painting (if any) (if any)
(if any)

9095883.doc 18
Giáo trình Java

5. Threads and Swing


Nếu làm việc các thành phần mà những thành phần đó phụ thuộc hoặc ảnh
hưởng đến trạng thái của nó, ta cần phải thực hiện chúng trong một tiến trình
đơn. Tuy nhiên, những chương trình khác cần sử dụng phương thức
invokeLater để thực hiện việc gọi cá thành phần có liên quan trong tiến trình
ấy.
Nếu một chương trình tạo ra và chỉ làm việc trên GUI của chính nó, thì không
cần quan tâm về tiến trình. Ví dụ, chương trình là một applet, nó sẽ đảm bảo an
toàn khi lưu cấu trúc của nó trong phương thức init. Thậm chí, khi chương
trình là một ứng dụng như dưới đây, thì vẫn đảm bảo được sự an toàn nói trên:
//Thread-safe example
public class MyApplication {
public static void main(String[] args) {
JFrame f = new JFrame(...);
...//Add components to the frame here...
f.pack();
f.setVisible(true);
//Don't do any more GUI work here.
}

...
//All manipulation of the GUI -- setText, getText, etc. --
//is performed in event handlers such as
actionPerformed().
...
}
6. Những tính năng và khái niệm khác của Swing
Swing cung cấp nhiều tính năng. Rất nhiều tính năng được cung cấp bởi
JComponent class. Một vài tính nằng thú vị sẽ không được đề cập đến trong
bài học này như icons, actions, công nghệ Pluggable Look & Feel, assistive
technologies, và separate models.
6.1 Những tính năng của Jcomponent (Features that
JComponent Provides)

9095883.doc 19
Giáo trình Java
Ngoại trừ đối tượng chứa mức đỉnh, tất cả các thành phần khác bắt đầu bằng ký
tự J đều được thừa kế từ lớp Jcomponent. Hầu hết các thành phần đều có các
tính năng chung như tooltips và cấu hình về giao diện (look and feel). Ngoài ra,
chúng còn thừa kế nhiều phương thức tiện lợi khác nữa.
6.2 Icons
Nhiều thành phần Swing, đặt biệt là button và label, có thể hiển thị hình ảnh.
Ta có thể chỉ định cho các hình ảnh này như là các đối tượng icon
6.3 Actions
Với đối tượng Action, Swing API cung cấp những hỗ trợ đặc biệt cho việc chia
xẽ dữ liệu và trạng thái giữa hai hoặc nhiều thành phần phát ra các sự kiện
hành động. Ví dụ, khi ta có một button và một menu item cùng một chức năng,
lúc đos cần cân nhắc việc sử dụng đối tượng Action để xác định văn bản, icon
và trạng thái của hai thành phần.
6.4 Support for Assistive Technologies
Assistive technologies such as screen readers can use the Accessibility API to
get information from Swing components. Because support for the Accessibility
API is built into the Swing components, your Swing program will probably
work just fine with assistive technologies, even if you do nothing special. With
very little extra effort, however, you can make your program function even
more smoothly with assistive technologies, which might well expand its
market. See How to Support Assistive Technologies for details.
7. Phân tích một chương trình Swing
Bao gồm một ứng dụng nhỏ về Swing với tên gọi là Converter sẽ mô tả
cách một chương trình Swing làm việc và mối quan hệ gắn kết nhau giữa các
đoạn mã trong chương trình.
Converter là một ứng dụng dùng để chuyển đổi đơn vị đo lường giữa hai hệ
thống met và U.S units. Để chạy được ứng dụng này thì phải biên dịch các tập
tin sau: Converter.java, ConversionPanel.java, ConverterRangeModel.java,
DecimalField.java, FollowerRangeModel.java,FormattedDocument.java và
Unit.java.
Sau đây là hình ảnh minh hoạ về giao diện của Converter:

9095883.doc 20
Giáo trình Java

Trong các bài học sau, chúng ta sẽ tìm hiểu chi tiết về các tính năng, khái niệm về
Swing. Chắc chắn sẽ còn nhiều thú vị đang chờ đợi chúng ta

9095883.doc 21
Giáo trình Java

Bài 3: Sử dụng các tính năng khác của Swing


Bài học này hướng dẫn cách sử dụng các tính năng khác của Swing.
1. Cách sử dụng Actions
Với đối tượng Action, ta có thể sắp xếp và quản lý các trạng thái của hai hoặc
nhiều thành phần tạo ra sự kiện của hành vi. Ví dụ, bạn có thể sử dụng Action
để tạo ra và quản lý một button trên thanh công cụ, một menu biểu tượng cùng
thực hiện một chức năng.
Sau đây là ví dụ của việc sử dụng Action để tạo ra một button trên thanh công
cụ và một menu item cùng thực hiện một chức năng:
Action leftAction = new <a class that implements
Action>(...);
JButton button = toolBar.add(leftAction);
JMenuItem menuItem = mainMenu.add(leftAction);
Đối với button hay menu item, để có được những hữu ích thực sự của việc sử
dụng Action, ta phải tạo thành phần sử dụng phương thức add(Action) của
JToolBar, JMenu, hoặc JPopupMenu. Mặc định, không có hàm API nào tồn tại
ở bên trên addActionListener(ActionListener) để kết nối một Action với một
thành phần khác đang tồn tại thực sự.
Để tạo một đối tượng Action, nói chung là ta phải tạo một lớp con của lớp
AbstractAction và thực thi nó. trong lớp con này, ta cho thực hiện phương thức
actionPerformed để tác động ngược trở lại một cách thoả đáng khi hành vi sự
kiện xảy ra. Sau đây là ví dụ của việc tạo và thực hiện lớp con của lớp
AbstractAction:
leftAction = new AbstractAction("Go left",
new ImageIcon("images/left.gif")) {
public void actionPerformed(ActionEvent e) {
displayResult("Action for first button/menu item", e);
}
};
Hình dưới đây là demo ứng dụng của việc sử dụng Action để thực hiện ba tính
năng.

9095883.doc 22
Giáo trình Java
Đây là những gì nhìn thấy khi menu "Go left" trở nên disabled:

Đoạn mã của "Go left" action:


boolean selected = ...//true if the action should be
enabled;
//false, otherwise
leftAction.setEnabled(selected);
Sau khi tạo ra thành phần sử dụng Action, ta có thể tùy biến chúng theo ý thích
của mình. Ví dụ như khi muốn thêm vào dòng tooltip của một button, hoặc tùy
biến việc xuất hiện hay biến mất của cá thành phần bằng cách thêm vào , xoá
đi các icon, dòng văn bản:
button = toolBar.add(leftAction);
button.setText(""); //an icon-only button
button.setToolTipText("This is the left button");
menuItem = mainMenu.add(leftAction);
menuItem.setIcon(null); //arbitrarily chose not to use icon
in menu

The Action API


Bảng sau đây liệt kê những phương thức và contructors thường dùng các
Action. Các hàm API sử dụng đối tượng Action được chia thành hai loại như
sau:
• Tạo và sử dụng một Action
• Tạo một thành phần điều khiển Action (Action-Controlled Component)
Creating and Using an Action
Constructor or Method Purpose
AbstractAction()
AbstractAction(Strin Tạo một đối tượng Action. Thông qua các tham số
g) của phương thức hay Contructors, có thể xác lập
AbstractAction(Strin văn bản hay icon trong thành phần ấy.
g, Icon)
void Xác lập hay nhận biết cá thành phần có nhận được

9095883.doc 23
Giáo trình Java
tác động hay không. Thông qua phương thức
setEnabled(boolean setEnabled(false), vô hiệu tất cả các tác động lên
) các thành phần. Tương tự như vậy, sử dụng phương
boolean isEnabled() thức setEnabled(true) để tác động lại hành vi của
các thành phần.
Creating an Action-Controlled Component
Method Purpose
JMenuItem
add(Action)
JMenuItem Tạo một đối tượng JMenuItem và đặt chúng vào
insert(Action, int) trong menu hay popup menu.
(in JMenu and
JPopupMenu)
JButton add(Action) Tạo một đối tượng Jbutton và đặt chúng lên thanh
(in JToolBar) công cụ.
2. Thế nào là kỹ thuật hỗ trợ Assisive(Support Assistive
Technologies)
Các thành phần Swing hỗ trợ kỹ thuật trợ giúp. Chương trình của bạn sẽ được
hỗ trợ tốt hơn. Ví dụ như dòng tooltip sẽ hiện lên chức năng của một nút lệnh
nào đó khi ta di chuyển chuột lên nút lệnh đó.

3. Cách sử dụng các đường viền(Border)


Borders sẽ đem lại cho chúng ta nhiều thuận lợi trong việc vẽ các đường thẳng,
tiêu đề hay các vùng trống của một thành phần. Trong các ví dụ của phần này
có sử dụng rất nhiều border. Ở đây, chúng ta sẽ tìm hiểu cách thêm một border
vào bất kỳ thành phần JComponent nào.
4. Cách sử dụng Icons
Nhiều thành phần Swing có thể hiển thị icons(JLabel và JButton). Thường
thì icons là trường hợp cá biệt của ImageIcon class.
Một vài thành phần Swing như JLabel và JButton, có thể được trang trí bởi
một icon. Icon là một đối tượng gắn kết chặt chẽ với giao diện Icon. Swing
cung cấp cho giao diện Icon cách thực hiện rất đặt biệt và hiệu quả đẻ vẽ một
Icon tưg một tập tin ảnh có dạng thức GIF hoặc JPEG.
Hình dưới đây minh hoạ một ứng dụng sử dụng một Icon để trang trí cho hai
label:

9095883.doc 24
Giáo trình Java

Trong phần mã của chương trình, câu lệnh (1) dùng để tạo icon sử dụng một
icon, câu lệnh (2) và (3) gán icon ấy vào trong hai label::
(1) ImageIcon icon = new ImageIcon("images/middle.gif",
"a pretty but meaningless splat");
...
(2) label1 = new JLabel("Image and Text", icon,
JLabel.CENTER);
...
(3) label3 = new JLabel(icon);
Tham số thứ nhất trong ImageIcon constructor xác định tập tin ảnh để nạp
lên, phần này phải đẻ ý đến đường dẫn tới thư mục có chứa tập tin class. Tham
số thứ hai dùng để mô tả về icon ấy, giống như phần tooltip của các ứng dụng
mà chúng ta vẫn thường thấy.
Nói chung, các applet nạp hính ảnh từ máy tính, nơi phục vụ cho applet ấy. Có
hai lý do để làm như vậy, thứ nhất là không tin tưởng khi để các applet đọc các
tập tin hệ thông từ máy nó đang chạy. Thứ hai là để kết hợp các lớp của applet
với tập tin dữ liệu với nhau. Để nạp một hình ảnh từ server, một applet phải sử
dụng URL như đoạn mã trong ví dụ dưới đây:
public class SomeClass extends JApplet ... {
protected String leftButtonFilename =
"images/left.gif";
...
public void init() {
...
URL leftButtonURL = getURL(leftButtonFilename);
...
leftButtonIcon = new ImageIcon(leftButtonURL,
"an arrow pointing left");
...
}
...
protected URL getURL(String filename) {
URL codeBase = getCodeBase();
URL url = null;

try {
url = new URL(codeBase, filename);

9095883.doc 25
Giáo trình Java
} catch (java.net.MalformedURLException e) {
System.err.println("Couldn't create image: " +
"badly specified URL");
return null;
}

return url;
}
...
}
• Với mỗi image icon, nó sử dụng một đối tượng image để để chứa dữ
liệu của hình ảnh và đối tượng MediaTracker được chia sẽ cho tất cả các
icon trong cùng một chương trình.
Xác định hình ảnh nguồn (Specifying the Image Source)
Thường thì dữ liệu của một hình ảnh xuất phát từ một tập tin hình ảnh. Có thể
xác định nơi lưu trữ của tập tin thông qua tên tập tin hoặc sử dụng đối tượng
URL. Đối với ứng dụng, tên tập tin hoặc URL đều có liên quan đến thư mục
chứa chứa tập tin .class của ứng dụng hoặc là đường dẫn. Để chỉ định một URL
liên quan đến đường dẫn của ứng dụng, ta có thể sử dụng phương thức
getSystemResource như ở ví dụ dưới đây:
ImageIcon icon = null;
URL iconURL =
ClassLoader.getSystemResource("images/middle.gif");
if (iconURL != null) {
icon = new ImageIcon(iconURL,
"a beautiful yet meaningless icon");
}
Phương thức getSystemResource sẽ dò tìm trong thư mục và tập tin JAR trong
đường dẫn của chương trình, trả về URL ngay khi nó tìm thấy. Ví dụ, khi ta
đưa vào đường dẫn của ứng dụng tập tin jar có tên icons.jar, nếu tập tin JAR có
chứa images/middle.gif, thì dứat khoát, đường dẫn sẽ trả về một URL xác định
cho images/middle.gif. Tuy nhiên, có thể là URL đó không có bất cứ liên quan
nào về tập tin icons.jar.

The Image Icon API

9095883.doc 26
Giáo trình Java
Bảng sau đây liệt kê những cấu trúc và phương thức sử dụng thông thường của
ImageIcon. constructors and methods. Lưu ý là ImageIcon không có
nguồn gốc từ JComponent hay thậm chí là từ Component.
Setting, Getting, and Painting the Image Icon's Image
Method or
Purpose
Constructor
ImageIcon()
ImageIcon(byte[]
)
ImageIcon(byte[],
String)
ImageIcon(Image Tạo một ImageIcon instance, khởi tạo nó để chứa hình
) ảnh đã được xác lập. Tham số thứ nhất chỉ ra nguồn của
ImageIcon(Image hình ảnh như image, số byte, tên tập tin, hay URL. Từ đó
, String) hình ảnh sẽ được nạp lên. Nguồn của hình ảnh phải có
ImageIcon(String) dạng thức file được hỗ trợ bởi lớp java.awt.Image có
đuôi là GIF hoặc JPEG.
ImageIcon(String,
String)
ImageIcon(URL)
ImageIcon(URL,
String)
void
setImage(Image) Xác lập hoặc lấy image hiển thị bởi image icon.
Image getImage()
Vẽ ảnh của icon trong vùng đồ hoạ đã được chỉ địnhPaint
the image icon's image in the specified graphics context.
You would do this only if you're implementing a custom
void
component that performs its own painting. The
paintIcon(Compo
Component object is used as an image observer. You
nent, Graphics,
can rely on the default behavior provided by
int, int)
Component class and pass in any component. The two
int argments specify the x and y coordinates,
respectively.
Setting or Getting Information about the Image Icon
Method Purpose
void
setDescription(S
Set or get a description of the image. This description is
tring)
intended for use by assistive technologies.
String
getDescription()
int Get the width or height of the image icon in pixels.
getIconWidth()

9095883.doc 27
Giáo trình Java
int
getIconHeight()
Watching the Image Icon's Image Load
Method Purpose
void
setImageObserver(Image
Observer) Set or get an image observer for the image icon.
ImageObserver
getImageObserver()
Get the loading status of the image icon's
int getImageLoadStatus() image. The set of values returned by this
method are defined by MediaTracker.
5. Sử dụng tiến trình(Threads)
Sử dụng tiến trình là một công việc khó khăn. Do đó nếu có thể được thì bạn
nên tránh phần này. Tuy nhiên Thread có thể giúp cải tiến chương trình của bạn
bằng cách quản lý sự thực thi.
Nguyên tắc đầu tiên khi sử dụng threads là: tránh dùng chúng nếu như có thể.
Threads có thể rất khó sử dụng và chúng có thể gây khó khăn khi chúng ta
debug chương trình. Để tránh trường hợp bị đình trệ của chương trình, cần phải
quan tâm ngay từ đầu rằng mỗi tiến trình được tạo ra không kêu gọi thực thi
bất kỳ một thành phần Swing nào.
Mặc dù nguy hiểm, nhưng threads rất cso giá trị khi ta sử dụng chúng một cách
cẩn thận. Ta có thể cải thiện việc thực hiện chương trình. Đôi khi, một vài tiến
trình làm đơn giản mã hoặc cấu trúc của một chương trình. Sau đây là một vài
tình huống khi sử dụng tiến trình:
• To move a time-consuming initialization task out of the main thread, so
that the GUI comes up faster. Examples of time-consuming tasks
include making extensive calculations and blocking for network or disk
I/O (loading images, for example).
• To move a time-consuming task out of the event-dispatching thread, so
that the GUI remains responsive.
• To perform an operation repeatedly, usually with some predetermined
period of time between operations.
• To wait for messages from other programs.
Nếu tạo một thread, cần phải tránh những nguy hiểm khi thực thi một tiến trình
với các lớp tiện ích như SwingWorker hay Timer. Đối tượng SwingWorker tạo
một thread để thực thi một qui trình xữ lý về thời gian. Sau khi qui trình hoàn
thành, SwingWorker cung cấp một vài tùy chọn để thực hiện việc gửi đi một sự

9095883.doc 28
Giáo trình Java
kiện. Đối tượng Timer thực thi một thread và sinh ra một hoặc nhiều hành vi sự
kiện sau khi xác định được lặp lại.
6. Sử dụng Timers
Với lớp Timer, bạn có thể cho thực hiện một tiến trình của việc thực thi một
hành động sau một khoảng thời gian xác định và lặp lại hành vi ấy.
Có hai cách để thực hiện Timer:
• Thực hiện một tác vụ, với thời gian lặp lại được xác định. Ví dụ, tool tip
manager sử dụng timers để quyết định khi nào thì hiển thị và khi nào thì
tắt nó đi.
• Thực hiện việc lặp đi lặp lại một tác vụ.

Trong ví dụ dưới đây sử dụng đối tượng timer để thể hiện tiến trình làm việc
của một tác vụ.

Và đây là đoạn mã của chương trình ProgressBarDemo.java.


public final static int ONE_SECOND = 1000;
...
timer = new Timer(ONE_SECOND, new ActionListener()
{
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
});
Khi user nhấn vào nút Start, chương trình khởi động timer:
timer.start();
Và khi tác vụ hoàn thành, action listener của timer sẽ dừng timer:
if (/* task is done */) {
...
timer.stop();
...
}
The Timer API

9095883.doc 29
Giáo trình Java
Bảng sau đây liệt kê những cấu trúc và phương thức của Timer. Các hàm API
về sử dụng timers chia thành hai loại như sau:
• Tạo và khởi động Timer
• Chạy một Timer
Creating and Initializing the Timer
Method or
Purpose
Constructor
Tạo một timer. Tham số int chỉ rõ thời gian dừng
(milliseconds) giữa hai hành vi sự kiện. Sử dụng setDelay
để thay đổi độ trể. Tham số thứ hai là một action listener, là
Timer(int,
một constructor dùng để nhận biết với một timer. Ngoài ra,
ActionListener)
còn có thể đăng ký action listeners với
addActionListener và gỡ bỏ chúng bằng
removeActionlistener.
void
setDelay(int) Xác lập hoặc lấy số milliseconds.
int getDelay()
void
setInitialDelay(i
nt) Xác lập hoặc lấy số milliseconds chờ trước khi bắt đầu hành
int vi sự kiện thứ nhất.
getInitialDelay(
)
void
setRepeats(boo Xác lập hoặc chỉ ra timer có lặp lại hay không. Mặc định có
lean) giá trị true. Gọi setRepeats(false) để khởi đầu cho một
boolean timer khởi động và kết thúc một hành.
isRepeats()
void
setCoalesce(bo Xác lập hoặc chỉ ra timer có liên tục hay không. Gửi một
olean) hành vi sự kiện vào một hành vi sự kiện đơn. Mặc định có
boolean gái trị true.
isCoalesce()
Running the Timer
Method Purpose
void start()
Khởi động timer. restart còn có thể thoát bất kỳ một hành vi
void
sự kiện nào được gửi tới.
restart()
void stop() Dừng hoạt động của một timer.
boolean
Kiểm tra xem có timer.
isRunning()

9095883.doc 30
Giáo trình Java

Bài 4: Bố trí các thành phần bên trong các đối tượng chứa
Bài học này sẽ hướng dẫn bạn cách quản lý việc bày trí mà Java Platform cung
cấp, cách sử dụng vị trí tuyệt đối.
1. Sử dụng Layout Managers
Phần này cung cấp các qui tắc tổng quan và chi tiết lệnh trong việc sử dụng
việc quản lý bố trí mà Java platform cung cấp.
a. Sử dụng Layout Managers
Sử dụng BorderLayout
Sau đây là một Applet cho thấy BorderLayout làm việc như thế nào.
setLayout(new BorderLayout());
setFont(new Font("Helvetica", Font.PLAIN, 14));

add("North", new Button("North"));


add("South", new Button("South"));
add("East", new Button("East"));
add("West", new Button("West"));
add("Center", new Button("Center"));
Quan trọng: khi thêm một thành phần vào một Container sử
dụng BorderLayout, bạn nên dùng phương thức add() hai
thông số, và thông số thứ nhất phải là "North", "South", "East",
"West", hoặc "Center". Nếu bạn sử dụng phương thức add()một
thông số hay bạn không xác lập thông số thứ nhất thì thành
phần đó sẽ không hiển thị.
Theo mặc định, BorderLayout không đặt khoảng trống giữa các
thành. Muốn vậy, bạn phải xác lập nó bằng cách dùng cấu trúc
sau:
public BorderLayout(int horizontalGap, int verticalGap)
Sử dụng CardLayout
Sau đây là một Applet cho thấy CardLayout làm việc như thế nào.
//Where instance variables are declared:
Panel cards;
final static String BUTTONPANEL = "Panel with Buttons";
final static String TEXTPANEL = "Panel with TextField";

//Where the container is initialized:


cards = new Panel();
cards.setLayout(new CardLayout());

...//Create a Panel named p1. Put buttons in it.


...//Create a Panel named p2. Put a text field in it.

cards.add(BUTTONPANEL, p1);

9095883.doc 31
Giáo trình Java
cards.add(TEXTPANEL, p2);
Khi bạn thêm một thành phần vào một Container mà có sử dụng CardLayout,
bạn phải sử dụng phương thức add() hai thông số: add(String name,
Component comp). Thông số thứ nhất có thể bất kì chuỗi nào để nhận ra
thành phần được thêm vào.
Sau đây là một đoạn mã ví dụ cho phương thức trên:
//Where the container is initialized:
...
//Put the Choice in a Panel to get a nicer look.
Panel cp = new Panel();
Choice c = new Choice();
c.addItem(BUTTONPANEL);
c.addItem(TEXTPANEL);
cp.add(c);
add("North", cp);

...

public boolean action(Event evt, Object arg) {


if (evt.target instanceof Choice) {
((CardLayout)cards.getLayout()).show(cards,(String)
arg);
return true;
}
return false;
}
Như đoạn mã trên, bạn có thể sử dụng phương thức show() của CardLayout
để xác lập thành phần hiển thị hiện tại. Thông số thứ nhất của phương thức
show() là Container mà CardLayout điều khiển. thông số thứ hai là chuỗi để
xác định thành phần hiển thị. Chuỗi này giống như chuỗi của thành phần thêm
vào Container.
Theo sau là tất cả các phương thức của CardLayout mà có thể
cho phép chọn một thành phần. cho mỗi phương thức, thông
số thứ nhất Container cho CardLayout là một Layout Manager.
public void first(Container parent)
public void next(Container parent)
public void previous(Container parent)
public void last(Container parent)
public void show(Container parent, String name)
Sử dụng FlowLayout
Sau đây là một Applet cho thấy FlowLayout hoạt động như thế nào.
setLayout(new FlowLayout());
setFont(new Font("Helvetica", Font.PLAIN, 14));
add(new Button("Button 1"));
add(new Button("2"));

9095883.doc 32
Giáo trình Java
add(new Button("Button 3"));
add(new Button("Long-Named Button 4"));
add(new Button("Button 5"));
Lớp FlowLayout có ba cấu trúc:
public FlowLayout()
public FlowLayout(int alignment)
public FlowLayout(int alignment,
int horizontalGap, int verticalGap)
thông số alignment phải là các giá trị FlowLayout.LEFT,
FlowLayout.CENTER, hoặc FlowLayout.RIGHT. Thông số horizontalGap và
verticalGap xác định số Pixel đặc giữa các thành phần. Nếu bạn không xác lập giá
trị này, FlowLayout sẽ mặc định giá tri 5 cho mỗi thông số.
Sử dụng GridLayout
Sau đây là một Aplet cho thấy GridLayout làm việc như thế nào.
//Construct a GridLayout with 2 columns and an
unspecified number of rows.
setLayout(new GridLayout(0,2));
setFont(new Font("Helvetica", Font.PLAIN, 14));

add(new Button("Button 1"));


add(new Button("2"));
add(new Button("Button 3"));
add(new Button("Long-Named Button 4"));
add(new Button("Button 5"));
Cấu trúc trên cho thấy lớp GridLayout tạo một đối tượng có hai cột và nhiều
hàng. Đây là một trong hai cấu trúc cho GridLayout. Sau đây là cách khai báo
cho cả hai cấu trúc này:
public GridLayout(int rows, int columns)
public GridLayout(int rows, int columns,
int horizontalGap, int verticalGap)
Sử dụng GridBagLayout
Theo sau là một vài đoạn lệnh tiêu biểu trong một Container có sử dụng
GridBagLayout.
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gridbag);

//For each component to be added to this container:


//...Create the component...
//...Set instance variables in the GridBagConstraints
instance...
gridbag.setConstraints(theComponent, c);
add(theComponent);
Bạn có thể sử dụng lại một đối tượng của GridBagConstraints cho nhiều thành
phần khác nhau, ngay cả khi các thành phần đó có sự ràng buộc khác nhau.

9095883.doc 33
Giáo trình Java
GridBagLayout rút ra một giá trị ràng buộc và không dùng lại
GridBagConstraints. Bạn phải cẩn thận, tuy nhiên, để khởi tạo lại giá trị của
một đối tượng GridBagConstraints làm giá trị mặc định khi cần thiết.
Bạn có thể xác lập các giá trị sau:
gridx, gridy
Xác định hàng và cột tại vị trí trên bên tái của thành phần. Hầu hết cột trên bên
tải có địc chỉ gridx=0, và hàng trên cùng có địa chỉ gridy=0. Sử dụng
GridBagConstraints.RELATIVE (giá trị mặc định) để xác định rằng thành
phần đó chỉ ở bên phải hay ở phía dưới.
gridwidth, gridheight
xác lập số cột hoặc số hàng trong vùng hiển thị của thành phần. những giá trị
này xác định số Cell mà thành phần sử dụng, không phải số Pixel nó sử dụng.
Mặc định là 1. Sử dụng GridBagConstraints.REMAINDER để xác định
thành phần đang ở hàng cuối cùng hay cột cuối cùng. Sử dụng
GridBagConstraints.RELATIVE để xác định bước kế tiếp của thaǹh phần
là hàng cuối hay cột cuối cùng.
fill
Được sử dụng khi vùng hiển thị của thành phần lớn hơn kich thước thành phần
đòi hỏi để quyết định khi nào hoặc thay đổi kích thước như thế nào. các giá trị
thích hợp là GridBagConstraints.NONE (mặc định),
GridBagConstraints.HORIZONTAL,
GridBagConstraints.VERTICAL và GridBagConstraints.BOTH.
ipadx, ipady
xác định phần phụ ở bên trong: bao nhiêu để thêm vào kích thước tối thiểu của
thành phần. giá trị mặc định là 0. Chiều rộng của thành phần tối thiểu nhất là
bằng chiều rộng tối thiểu của nó cộng với ipadx*2. Similarly, chiều cao của
thành phần tối thiểu nhất là bằng chiều cao tối thiểu của nó cộng với ipady*2.
insets
xác định phần phụ bên ngoài của thành phần. mặc định, mỗi thành phần không
có phần phụ bên ngoài.
anchor
được sử dụng khi thành phần nhỏ hơn vùng hiển thị để quyết định khi nào đặt
thành phần. gái trị thích hợp là GridBagConstraints.CENTER (mặc định),
GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST,
GridBagConstraints.EAST, GridBagConstraints.SOUTHEAST,
GridBagConstraints.SOUTH, GridBagConstraints.SOUTHWEST,
GridBagConstraints.WEST, và GridBagConstraints.NORTHWEST.
Ví dụ :
Sau đây là một Applet chỉ cho thấy GridBagLayout hoạt động như thế nào.
Sau đây là một đoạn lệnh tạo một GridBagLayout và các thành
phần nó quản lí
protected void makebutton(String name,
GridBagLayout gridbag,
GridBagConstraints c) {
Button button = new Button(name);

9095883.doc 34
Giáo trình Java
gridbag.setConstraints(button, c);
add(button);
}

public GridBagWindow() {
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();

setFont(new Font("Helvetica", Font.PLAIN, 14));


setLayout(gridbag);

c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
makebutton("Button1", gridbag, c);
makebutton("Button2", gridbag, c);
makebutton("Button3", gridbag, c);

c.gridwidth = GridBagConstraints.REMAINDER; //end of


row
makebutton("Button4", gridbag, c);

c.weightx = 0.0; //reset to the default


makebutton("Button5", gridbag, c); //another row

c.gridwidth = GridBagConstraints.RELATIVE; //next to


last in row
makebutton("Button6", gridbag, c);

c.gridwidth = GridBagConstraints.REMAINDER; //end of


row
makebutton("Button7", gridbag, c);

c.gridwidth = 1; //reset to the default


c.gridheight = 2;
c.weighty = 1.0;
makebutton("Button8", gridbag, c);

c.weighty = 0.0; //reset to the default


c.gridwidth = GridBagConstraints.REMAINDER; //end of
row
c.gridheight = 1; //reset to the default
makebutton("Button9", gridbag, c);
makebutton("Button10", gridbag, c);
}

9095883.doc 35
Giáo trình Java

2. Tạo một Custom Layout Manager


Thay vì sử dụng cách quản lý mà Java platform cung cấp, ta có thể viết một
chương trình quản lý của chính mình. Quản lý bố trí phải thực thi
LayoutManager interface, nơi chỉ định năm phương thức phải được định
nghĩa. Việc quản lý cách bố trí có thể thực thi LayoutManager2, là một
giao diện con của LayoutManager.
Năm phương thức được thực thi là:
(1) void addLayoutComponent(String, Component)
(2)void removeLayoutComponent(Component)
(3)Dimension preferredLayoutSize(Container)
(4)Dimension minimumLayoutSize(Container)
(5)void layoutContainer(Container)
public void addLayoutComponent(String name, Component
comp)
Chỉ được gọi bằng phương thức add(name, component) của Container.
public void removeLayoutComponent(Component comp)
Gọi bởi những phương thức remove() và removeAll() của Container.
public Dimension preferredLayoutSize(Container parent)
Gọi bởi phương thức preferredSize() của Container, có thể tự gọi dưới mọi
tình huống.
public Dimension minimumLayoutSize(Container parent)
Gọi bởi phương thức minimumSize() của Container, có thể tự gọi dưới mọi
tình huống.
public void layoutContainer(Container parent)
Gọi khi Container hiển thị lần đầuis first displayed, và mọi lúc nó tahy đổi kích
thước.
3. Làm việc không có Layout Manager(Absolute Positioning)
If necessary, you can position components without using a layout manager.
Generally, this solution is used to specify absolute sizes and positions for
components.
Mặc dù có thể làm việc mà không cần Layout Manager, bạn nên dùng Layout
Manager nếu có thể. Layout managers dể thay đổi kích thước của Container và
điều chỉnh hình dạng của các thành phần phụ thuộc vào Platform. Nó cùng có
thể được sử dụng lạ bới các Container va các chương trình khác. nếu Custom
Container sè không tái sử dụng, không thể thay đổi kích thước, và hoàn toàn có
thể điều khiển được các thông số phụ thuộc vào hệ thống như Font và hình
dạng các thành phần.
Ví dụ:
public class NoneWindow extends Frame {
...

9095883.doc 36
Giáo trình Java
private boolean laidOut = false;
private Button b1, b2, b3;

public NoneWindow() {
super();
setLayout(null);
setFont(new Font("Helvetica", Font.PLAIN, 14));

b1 = new Button("one");
add(b1);
b2 = new Button("two");
add(b2);
b3 = new Button("three");
add(b3);
}

public void paint(Graphics g) {


if (!laidOut) {
Insets insets = insets();
/*
* We're guaranteed that insets() will return a valid
Insets
* if called from paint() -- it isn't valid when called
from
* the constructor.
*
* We could perhaps cache this in an ivar, but
insets can
* change, and when they do, the AWT creates a
whole new
* Insets object; the old one is invalid.
*/
b1.reshape(50 + insets.left, 5 + insets.top, 50,
20);
b2.reshape(70 + insets.left, 35 + insets.top, 50,
20);
b3.reshape(130 + insets.left, 15 + insets.top, 50,
30);

laidOut = true;
}
}

...
}

9095883.doc 37
Giáo trình Java
4. Giải quyết các vấn đề về Layout
Một vài vấn đề thông thường về layout mà thường là các thành phần hiển thị
quá nhỏ hoặc không hiển thị. Trong phần này sẽ giúp chúng ta xử lý những vấn
đề này.
Bài toán: Làm thế nào để xác định được chính xác kích thước của một thành
phần?
• Đầu tiên, chắc chắn bạn thật sự muốn xác lập kích thước chính xác của
thành phần. những thành phần chuẩn có kích thước khác nhau, phụ
thuộc vao Platform mà thành phần đó đang chạy và Font nó sử dụng, vì
vậy thwongf chỉ làm theo cảm giác để xác định kích thước chính xác
của các thành phần.
Đối với những Custom Component có kích thước xác
định, xác định kích thước chính xác chỉ là cảm giác chủ
quan. Bạn cần bỏ qua các phương thức minimumSize() và
preferredSize() của thành phần để trả về một kích thước
đúng cho thành phần đó.
Để thay đổi kích thước của thành phần khi thành phần đó
đang hiển thị, xem bài toán tiếp theo.
Bài toán: Làm thế nào để thay đổi kích thước của một thành phần?
• Một khi thành phần đã hiển thị, bạn có thể thay đổi kích thước của nó
bằng phương thức resize(). Rồi bạn gọi phương thức validate() để
Container vè lại.
Bài toán: Thành phần đang có kích thước quá nhỏ.
• Thành phần đó có thực thi những phương thức preferredSize() và
minimumSize() hay không? Nếu vậy, nó có trả về giá trị đúng hay
không?
• Khi bạ dùng Layout manager, bạn có thể dùng không gian sẵn có hay
không?
Bài 5: Viết sự kiện Listeners
Trong bài học này sẽ trình bày một cách chi tiết để làm thế nào viết một sự
kiện listeners. Để hiểu phần này thì bạn phải có kiến thức về Event Handling.
1. Một vài ví dụ về Event-Handling
Các applets trong phần này sẽ minh hoạ cho các sự kiện và quá trình tiến hành
của sự kiện.
2. Tổng quan về Writing Event Listeners
Cung cấp thông tin cần thiết về tất cả các kiểu của sự kiện. Một trong những
tiêu đề trong phần này là trình bày cách làm sao để giảm bớt công sức và sự
không cần thiết của việc viết code cho chương trình bằng cách sử dụng các lớp
trong để thực thi các sự kiện.

9095883.doc 38
Giáo trình Java
Để có thể nắm bắt phần này một cách dễ dàng, xem như bạn đã có những kiến
thức cơ bản về các sự kiện listener trong phần Event Handling. Chẵng hạn như
ta có thể gắn một đa listeners vào nguồn của một đơn sự kiện. Nhưng quan
trọng hơn hết là các phương thức event-listener sẽ được xử lý một cách nhanh
chóng. Bởi vì tất cả các event-handling và các phương thức vẽ đều được thực
hiện trong cung một tiến trình.
Trong phần này, chúng ta sẽ bàn về EventObject, một lớp con cho tất cả các
sự kiện AWT và Swing
Lấy thông tin sự kiện: Event Objects (Getting Event
Information: Event Objects)
Mỗi một phương thức event-listener đều có một đối số đơn, một đối tượng thừa
kế từ lớp EventObject. method has a single argument -- an object that inherits
from the EventObject class. Mặc dù đối số luôn xuất phát từ
EventObject, với kiểu tổng quan để có thể thực hiện chính xác hơn. Ví dụ
như khi nắm bắt sự kiện của chuột, đối số cho phương thức này sẽ lấy từ
MouseEvent, một lớp con của EventObject.
Lớp EventObject định nghĩa một phương thức rất hữu ích như
sau:
Object getSource()
Phương thức này trả về một đối tượng nắm bắt sự kiện.
Chú ý rằng phương thức getSource cũng trả về một đối tượng.
Lớp Event đôi khi cũng định nghĩa một phương thức giống như
getSource, nhưng kiểu của giá trị trả về hơi bị hạn chế. Ví dụ
như lớp ComponentEvent định nghĩa phương thức
getComponent, giống như getSource, trả về đối tượng nắm
bắt sự kiện. Cái khác nhau ở đây là getComponent luôn luôn
trả về một Component.
Thường thì một lớp sự kiện nào đó định nghĩa một phương thức
và trả về thông tin của sự kiện.
Khái niệm: Low-Level Events and Semantic Events
Các sự kiện có thể được phân chia thành 2 loại: low-level events và semantic
events. Low-level events mô tả window-system xảy ra hoặc dữ liệu vào ở mức
thấp (low-level input). Tất cả các sự kiện còn lại thuộc loại semantic event.
Sự kiện mouse và key, cả hai đều là kết quả trực tiếp từ phía người dùng, là
những sự kiện low-level. Những sự kiện low-level khác bao gồm component,
container, focus, và window events. Sự kiện component cho phép thay đổi vị
trí, kích thước và sự hiển thị của thành phần. Sự kiện container quản lý để nắm
bắt được thành phần nào khi được thêm vào hay gở bỏ khỏi các đối tượng
chứa. Focus events sẽ báo cho biết khi nào một thành phần là có hoặc không
keyboard focus, khả năng nhận biết ký tự được gõ tại bàn phím. Window

9095883.doc 39
Giáo trình Java
events giúp để nắm bắt những trạng thái căn bản nhất của bất kỳ Window nào,
chẳng hạn như Dialog hay một Frame.
Semantic events bao gồm action events, item events, và list selection events.
Hành động của mỗi semantic event có thể khác nhau do thành phần. Ví dụ như
một button có thể nắm bắt sự kiện khi người dùng kích chuột lên nó. Nhưng
một text field nắm bắt sự kiện khi người dùng nhấn Return.
Sử dụng Adapters and Inner Classes để nắm bắt các sự kiện

Phần này hướng dẫn bạn sử dụng các lớp adapters và inner để làm giảm bớt sự lộn
xộn trong đoạn mã của chương trình bạn.
Hầu hết các giao diện AWT listener, không như ActionListener, chứa nhiều hoặc
một phương thức. Ví dụ, giao diện MouseListener chứa năm phương thức:
mousePressed, mouseReleased, mouseEntered, mouseExited, và
mouseClicked. Dù là bạn chỉ quan tâm về nhấn chuột, nếu lớp bạn đang sử dụng
thực thi MouseListener thì bạn phải thực thi tất cả 5 phương thức.
Ví dụ :
//An example with cluttered but valid code.
public class MyClass implements MouseListener {
...
someObject.addMouseListener(this);
...
/* Empty method definition. */
public void mousePressed(MouseEvent e) {
}

/* Empty method definition. */


public void mouseReleased(MouseEvent e) {
}

/* Empty method definition. */


public void mouseEntered(MouseEvent e) {
}

/* Empty method definition. */


public void mouseExited(MouseEvent e) {
}

public void mouseClicked(MouseEvent e) {


...//Event handler implementation goes here...
}
}
Đáng tiếc là kết quả của sự lựa chọn các phương thức rỗng có thể khó đọc và duy trì.
Để giúp bạn tránh được các lộn xộn với những phương thức rỗng trong chương trình,

9095883.doc 40
Giáo trình Java
AWT cung cấp lớp adapter class cho mỗi listener interface với nhiều hơn một phương
thức.
Để sử dụng adapter, bạn tạo một lớp con cho nó, thay vì phải thực thi một listener
interface.
/*
* An example of extending an adapter class instead of
* directly implementing a listener interface.
*/
public class MyClass extends MouseAdapter {
...
someObject.addMouseListener(this);
...
public void mouseClicked(MouseEvent e) {
...//Event handler implementation goes here...
}
}
Giả dụ bạn muốn viết một applet, và bạn muốn Applet của bạn chứa vài đoạn mã để
nắm bắt các sự kiện của chuột. Từ khi ngôn ngữ Java khhong cho phép đa thừa kế thì
bạn không thể mở rộng cả 2 lớp Applet and MouseAdapter. Giải pháp là định
nghĩa một lớp inner -- một lớp nằm trong Aplet -- that extends the MouseAdapter
class,
//An example of using an inner class.
public class MyClass extends Applet {
...
someObject.addMouseListener(new MyAdapter());
...
class MyAdapter extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
...//Event handler implementation goes here...
}
}
}
3. Hỗ trợ Listeners của các thành phần Swing
Có thể nói rằng loại của sự kiện một thành phần có thể được phân loại bằng
cách dựa vào loại của sự kiện listeners mà ta đăng ký trên thành phần đó. Ví dụ
như lớp Component định nghĩa nhýÞng phương thức listener nhý sau:
• addComponentListener
• addFocusListener
• addKeyListener
• addMouseListener
• addMouseMotionListener
9095883.doc 41
Giáo trình Java
Do vậy, mỗi thành phần hỗ trợ component, focus, key, mouse, và mouse-
motion listeners. Tuy nhiên, một thành phần khởi động những sự kiện mà
listeners có đắngky trên nó. Ví dụ, một mouse listener được đăng ký trên một
thành phần riêng biệt, nhưng thành phần ấy không có listeners khác, thì thành
phần đó sẽ khởi động chỉ mỗi sự kiện mouse events, không có các sự kiện
component, focus, key, or mouse-motion.
Listeners that All Swing Components Support
Vì tất cả các thành phần Swing đều xuất phát từ lớp AWT Component, cho
nên ta phải khai báo những listeners sau trên bất kỳ thành phần Swing nào:
component listener
Nắm bắt sự thay đổi về kích thước, vị trí và sự hiển thị của thành phần. focus
listener
Nắm bắt các thành phần có nhận hay không tác động từ bàn phím.
key listener
Nắm bắt động tác ấn phím; sự kiện key chỉ khởi tạo bởi các thành phần đang
có trạng thái mặc định của bàn phím.
mouse events
Nắm bắt sự kiện kích chuột và di chuyển chuột trên thành phần.
mouse-motion events
Nắm bắt sự thay đổi về vị trí của con trỏ trên thành phần.
Các Listeners khác mà các thành phần Swing hỗ trợ
Bảng sau đây liệt kê các thành phần Swing và listeners được hỗ trơ. Trong
nhiều trường hợp, các sự kiện được khởi động trực tiếp từ thành phần. Những
trường hợp khác, các sự kiện được khởi động từ dữ liệu của thành phần hoặc từ
các kiểu mẫu được chọn.
Listener
Component document,
chang list wind othe
action caret undoable item
e selection ow r
edit
button X X X
check box X X X
color chooser X
combo box X X
dialog X
h
ype
editor pane X X
rlin
k
file chooser X
frame X
internal frame inte
9095883.doc 42
Giáo trình Java
rnal
fra
me
list
list X
data
m
menu enu
m
enu
key
men
menu item X X X u
dra
g
mo
use
option pane
password field X X X
p
opu
popup menu p
men
u
progress bar X
radio button X X X
slider X
tabbed pane X

tabl
e
mo
del
tabl
e
table X colu
mn
mo
del
cell
edit
or

9095883.doc 43
Giáo trình Java
text area X X
text field X X X

hyp
text pane X X
erli
nk
toggle button X X X

tree
exp
ansi
on
tree
will
exp
tree
and
tree
mo
del
tree
sele
ctio
n
viewport
(used by X
scrollpane)
4. Thực hiện Listeners cho các Handled Events thông thường
Phần này sã bao gồm các chi tiết về ví dụ và thông tin của việc viết những sự
kiện listener thông thường.
Viết một Action Listener
Khi người sử dụng kích chuột vào Button, đúp chuột vào ListItem, chọn MenuItem,
hoặc nhấn phím trong TextField, một sự kiện sẽ xảy ra. Kết quả đó là một thông báo
actionPerformed được gởi đi đến tất cả các action listener và nó đăng kí với các
thành phần có liên quan.
Các phương thức, sự kiện của hành động
Giao diện ActionListener chứa một phương thức đơn, và do đó nó không có lớp
adapter tương ứng. Đây là phương thức ActionListener cô độc:
void actionPerformed(ActionEvent)
Một ví dụ về nắm bắt các sự kiện của hành động
Một ví dụ đơn giản
public class Beeper ... implements ActionListener {
...
//where initialization occurs:
9095883.doc 44
Giáo trình Java
button.addActionListener(this);
...
public void actionPerformed(ActionEvent e) {
...//Make a beep sound...
}
}

Viết một Adjustment Listener


Các sự kiện Adjustment thông báo cho bạn biết sự thay đổi giá trị trong các thành
phần. Đối tượng Adjustable có một giá trị nguyên, và nó trả về các các sự kiện
adjustment bất cứ khi nào giá trị đó thay đổi. Chỉ có một lớp của AWT thực thi
Adjustable là lớp Scrollbar.
Có 5 loại sự kiện adjustment:
track
người sử dụng hoàn toàn thay đổi giá trị của thành phần.
unit increment, unit decrement
người sử dụng chỉ biểu thị sự thay đổi nhỏ về giá trị của thành phần.
block increment, block decrement
người sử dụng biểu thị sự thay đổi giá trị của thành phần với số lượng lớn.
Các phương thức sự kiện của Adjustment
Giao diện Adjustment Listener chứa một phương thức đơn, và vì thế nó không có
lớp mô phỏng tương ứng. Sau đây là phương thức đó:
void adjustmentValueChanged(AdjustmentEvent)
Được gọi bởi AWT vừa sau khi thay đổi giá trị của thành phần.
Ví dụ về Handling Adjustment Events
class ConversionPanel ... implements AdjustmentListener ... {
...
Scrollbar slider;
...
ConversionPanel(...) {
...
slider.addAdjustmentListener(this);
}
...
/** Respond to the slider. */
public void adjustmentValueChanged(AdjustmentEvent e) {
textField.setText(String.valueOf(e.getValue()));
controller.convert(this);
}
...
}
Lớp AdjustmentEvent
Phương thức adjustmentValueChanged có một thông số: một đối tượng
AdjustmentEvent. Lớp AdjustmentEvent định nghĩa các phương thức sau:
Adjustable getAdjustable()

9095883.doc 45
Giáo trình Java
Trả về thành phần mà sinh ra sự kiện đó. Bạn có thể dùng nó thay vì dùng phương
thức getSource.
int getAdjustmentType()
Trả về kiểu của adjustment được tìm thấy. giá trị trả về là một trong những giá trị sau
được định nghĩa trong lớp AdjustmentEvent: UNIT_INCREMENT,
UNIT_DECREMENT, BLOCK_INCREMENT, BLOCK_DECREMENT, TRACK.
int getValue()
Trả về giá trị của thành phần ngay sau khi adjustment được tìm thấy.
Viết một Component Listener
là một trong những sự kịen của thành phần được phát ra bởi đối tượng Component
ngay sau khi thành phần đó mất đi, làm ẩn đi, chuyển vị trí hoặc thay đổi kích thước
Các phương thức, sự kiện của thành phần
Giao diện ComponentListener và lớp mô phỏng tương ứng,
ComponentAdapter, chứa 4 phương thức:
void componentHidden(ComponentEvent)
được gọi bởi AWT sau khi thành phần biến mất bởi phương thức setVisible.
void componentMoved(ComponentEvent)
được gọi bởi AWT sau khi thành phần di chuyển, nó quan hệ với đối tượng chứa nó.
void componentResized(ComponentEvent)
được gọi bởi AWT sau khi thành phần thay đổi kích thước.
void componentShown(ComponentEvent)
được gọi bởi AWT sau khi thành phần xuất hiện bởi phương thức setVisible.
Ví dụ về Handling Component Events
public class ComponentEventDemo ... implements ComponentListener {
...
//where initialization occurs:
aFrame = new Frame("A Frame");
ComponentPanel p = new ComponentPanel(this);
aFrame.addComponentListener(this);
p.addComponentListener(this);
...

public void componentHidden(ComponentEvent e) {


displayMessage("componentHidden event from "
+ e.getComponent().getClass().getName());
}

public void componentMoved(ComponentEvent e) {


displayMessage("componentMoved event from "
+ e.getComponent().getClass().getName());
}

public void componentResized(ComponentEvent e) {


displayMessage("componentResized event from "
+ e.getComponent().getClass().getName());

9095883.doc 46
Giáo trình Java
}

public void componentShown(ComponentEvent e) {


displayMessage("componentShown event from "
+ e.getComponent().getClass().getName());
}
}

class ComponentPanel extends Panel ... {


...
ComponentPanel(ComponentEventDemo listener) {
...//after creating the label and checkbox:
label.addComponentListener(listener);
checkbox.addComponentListener(listener);
}
...
}
Lớp ComponentEvent
Mỗi một phương thức của sự kiện các thành phần có một thông số đơn: đối tượng
ComponentEvent lớp ComponentEvent định nghĩa một phương thức hay
dùng, getComponent, trả về thành phần mà phát ra sự kiện.
Viết một Container Listener
Những sự kiện của Container được phát ra ngay sau khi một thành phần được thêm
vào Container hoặc chuyển đi khỏi Container.
Các phương thức, sự kiện của Container
Giao diện ContainerListener và lớp mô phỏng tương ứng, ContainerAdapter
chứa hai phương thức:
void componentAdded(ContainerEvent)
được gọi sau khi một thành phần được thêm vào Container.
void componentRemoved(ContainerEvent)
được gọi sau khi một thành phần được chuyển đi khỏi Container.
Ví dụ về Handling Container Events
public class ContainerEventDemo ... implements ContainerListener ... {
...//where initialization occurs:
buttonPanel = new Panel();
buttonPanel.addContainerListener(this);
...
public void componentAdded(ContainerEvent e) {
displayMessage(" added to ", e);
}

public void componentRemoved(ContainerEvent e) {


displayMessage(" removed from ", e);
}

9095883.doc 47
Giáo trình Java

void displayMessage(String action, ContainerEvent e) {


display.append(((Button)e.getChild()).getLabel()
+ " was"
+ action
+ e.getContainer().getClass().getName()
+ "\n");
}
...
}
Lớp ContainerEvent
Mỗi phương thức của Container Event có một thông số đơn: đối tượng
ContainerEvent. Lớp ContainerEvent định nghĩa hai phương thức thường dùng
sau:
Component getChild()
Trả về thành phần được thêm hay chuyển khỏi Container trong sự kiện này.
Container getContainer()
Tar về Container sinh ra sự kiện này.
Viết một Focus Listener
Các sự kiện Focus được phát ra khi một thành phần có hoặc mất đi sự tập trung vào
nó.
Các phương thức, sự kiện của Focus
Gaio diện FocusListener và lớp mô phỏng tương ứng, FocusAdapter, chứa hai
phương thức:
void focusGained(FocusEvent)
được gọi sau khi thành phần có sự tập trung.
void focusLost(FocusEvent)
được gọi sau khi thành phần mất sự tập trung.
Ví dụ về Handling Focus Events
public class FocusEventDemo ... implements FocusListener ... {
...//where initialization occurs
window = new FocusWindow(this);
...
public void focusGained(FocusEvent e) {
displayMessage("Focus gained", e);
}

public void focusLost(FocusEvent e) {


displayMessage("Focus lost", e);
}

void displayMessage(String prefix, FocusEvent e) {


display.append(prefix
+ ": "
+ e.getSource() //XXX

9095883.doc 48
Giáo trình Java
+ "\n");
}
...
}

class FocusWindow extends Frame {


...
public FocusWindow(FocusListener listener) {
super("Focus Demo Window");
this.addFocusListener(listener);
...
Label label = new Label("A Label");
label.addFocusListener(listener);
...
Choice choice = new Choice();
...
choice.addFocusListener(listener);
...
Button button = new Button("A Button");
button.addFocusListener(listener);
...
List list = new List();
...
list.addFocusListener(listener);
}
}
Lớp FocusEvent
Mỗi phương thức Focus Event có một thông số đơn : đối tượng FocusEvent. Lớp
FocusEvent định nghĩa một phương thức, isTemporary, trả về giá trị True khi sự
kiện mất sự tập trung đó là tạm thời.
Mọi thông báo thông thường mà bạn gởi tới đối tượng FocusEvent là
getComponent (được định nghĩa trong ComponentEvent), nó trả về thành
phần gây ra sự kiện này.
Viết một Item Listener
Các sự kiện của Item được phát ra khi thực thi giao diện ItemSelectable.
Các phương thức, sự kiện của Item
Giao diện ItemListener vhỉ có một phương thức, vì vậy nó không có lớp mô phỏng
tương ứng:
void itemStateChanged(ItemEvent)
được gọi sau khi thay đổi trạng thái của thành phần.
Ví dụ về Handling Item Events
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
label.setVisible(true);
} else {

9095883.doc 49
Giáo trình Java
label.setVisible(false);
}
}
Lớp ItemEvent
Mỗi phương thức của Item event có một thông số đơn: đối tượng ItemEvent. Lớp
ItemEvent định nghĩa các phương thức sau:
Object getItem()
Trả về Item đợc tập trung trong sự kiện này.
ItemSelectable getItemSelectable()
Tar về thành phần phát ra sự kiện.
int getStateChange()
trả về trạng thái mới của Item. Lớp ItemEvent định nghĩa hai trạng thái:
SELECTED và DESELECTED.
Viết một Key Listener
Đựoc phát ra khi người sử dụng đánh phím. Đặc biệt Key events phát ra bởi đối tượng
mà dang được tập trung khi người dùng nhấn hoặc nhả phím.
Các phương thức sự kiện của Key
Giao diện KeyListener và lớp mô phỏng tương ứng, KeyAdapter, chứa ba
phương thức:
void keyTyped(KeyEvent)
đựoc gọi sau khi phím đựoc đánh.
void keyPressed(KeyEvent)
được goị sau khi một phím được ấn.
void keyReleased(KeyEvent)
được gọi sau khi một phím được nhả.
Ví dụ về Handling Key Events
public class KeyEventDemo ... implements KeyListener ... {
...//where initialization occurs:
typingArea = new TextField(20);
typingArea.addKeyListener(this);
...
/** Handle the key typed event from the text field. */
public void keyTyped(KeyEvent e) {
displayInfo(e, "KEY TYPED: ");
}

/** Handle the key pressed event from the text field. */
public void keyPressed(KeyEvent e) {
displayInfo(e, "KEY PRESSED: ");
}

/** Handle the key released event from the text field. */
public void keyReleased(KeyEvent e) {
displayInfo(e, "KEY RELEASED: ");
}

9095883.doc 50
Giáo trình Java
...
protected void displayInfo(KeyEvent e, String s){
...
char c = e.getKeyChar();
int keyCode = e.getKeyCode();
int modifiers = e.getModifiers();
...
tmpString = KeyEvent.getKeyModifiersText(modifiers);

...//display information about the KeyEvent...


}
}
Lớp KeyEvent
Mỗi phương thức Key Event có một thông số đơn: đối tượng KeyEvent. Lớp
KeyEvent định nghĩanhững phương thức thường dùng sau:
int getKeyChar()
void setKeyChar(char)
Nhận hoặc xác lập kí tự liên quan với sự kiện này.
int getKeyCode()
void setKeyCode(int)
nhận hoặc xác lập mã của phím liên quan với sự kiện này.
void setModifiers(int)
xác lập tạng thái của phím liên quan tới sự kiện này
int getModifiers()
Tả về trạng thái của phím trong sự kiện này.
Viết một Mouse Listener
Các sự kiện được phát ra khi người sử dụng dùng chuột tác đông đến một thành phần.
Các phương thức, sự kiện của Mouse
Giao diện MouseListener và lớp mô phỏng tương ứng, MouseAdapter, chứa ba
phương thức:
void mouseClicked(MouseEvent)
được gọi sau nkhi người sử dụng kích hoạt chuột vào một thành phần.
void mouseEntered(MouseEvent)
được gọi sau khi con trỏ chuột nằm trong địa phận của thành phần.
void mouseExited(MouseEvent)
được gọi sau khi con trỏ chuột ra khỏi địa phận của thành phần.
void mousePressed(MouseEvent)
được gọi sau khi con chuột được ấn trên địa phận của thành phần.
void mouseReleased(MouseEvent)
được gọi sau khi con chuột được nhả trên địa phận của thành phần.
Ví dụ về Handling Mouse Events
public class MouseEventDemo ... implements MouseListener {
...//where initialization occurs:
//Register for mouse events on blankArea and applet (panel).
blankArea.addMouseListener(this);

9095883.doc 51
Giáo trình Java
addMouseListener(this);
...

public void mousePressed(MouseEvent e) {


saySomething("Mouse button press", e);
}

public void mouseReleased(MouseEvent e) {


saySomething("Mouse button release", e);
}

public void mouseEntered(MouseEvent e) {


saySomething("Cursor enter", e);
}

public void mouseExited(MouseEvent e) {


saySomething("Cursor exit", e);
}

public void mouseClicked(MouseEvent e) {


saySomething("Mouse button click", e);
}

void saySomething(String eventDescription, MouseEvent e) {


textArea.append(eventDescription + " detected on "
+ e.getComponent().getClass().getName()
+ ".\n");
textArea.setCaretPosition(maxInt); //scroll to bottom
}
}
Lớp MouseEvent
Mỗi phương thức Mouse Event có một thông số đơn: đối tượng MouseEvent. Lớp
MouseEvent định nghĩa các phương thức thường dùng sau:
int getClickCount()
trả về số lần nhấn liên tiếp của người sử dụng.
int getX()
int getY()
Point getPoint()
Trả về vị trí của con trỏ trỏ chuột, vị trí này phụ thuộc vào thành phần.
boolean isPopupTrigger()
trả về giá trị True khi sự kiện này làm xuất hiện Popup Menu.
Viết một Mouse Motion Listener
Các sự kiện Mouse motion phát ra ki người sử dụng dùng chuột di chuyển trên màn
hình.

9095883.doc 52
Giáo trình Java
Các phương thức, sự kiện của Mouse Motion
Giao diện MouseMotionListener và lớp mô phỏng tương ứng,
MouseMotionAdapter, chứa hai phương thức:
void mouseDragged(MouseEvent)
được gọi sau khi người sử dụng di chuyển chuột trong khi chuột đang được nhấn.
void mouseMoved(MouseEvent)
được gọi sau khi người sử dụng di chuyển chuột khi con chuột chưa bị nhấn.
Ví dụ về Handling Mouse Motion Events
...//where initialization occurs:
MyListener myListener = new MyListener();
addMouseListener(myListener);
addMouseMotionListener(myListener);
...
class MyListener extends MouseAdapter
implements MouseMotionListener {
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
currentRect = new Rectangle(x, y, 0, 0);
repaint();
}

public void mouseDragged(MouseEvent e) {


updateSize(e);
}

public void mouseMoved(MouseEvent e) {


//Do nothing.
}

public void mouseReleased(MouseEvent e) {


updateSize(e);
}

void updateSize(MouseEvent e) {
int x = e.getX();
int y = e.getY();
currentRect.setSize(x - currentRect.x,
y - currentRect.y);
repaint();
}
}

9095883.doc 53
Giáo trình Java
Các phương thức, sự kiện được sử dụng bởi Mouse-Motion Listeners
Mỗi phương thức Mouse Motion Event có một thông số đơn, và nó không đợc gọi là
MouseMotionEvent! Thay vào đó, phương thức Mouse Motion Event sử dụng đối
tượng MouseEvent.
Viết một Text Listener
Các sự kiện Text trả về sau khi chuỗi trong thành phần Text có sự thay đổi.
Các phương thức, sự kiện của Text
Giao diện TextListener chỉ có một phương thức nên không có lớp mô phỏng tương
ứng:
void textValueChanged(TextEvent)
được gọi sau khi chuỗi trong thành phần Text thay đổi.
Examples of Handling Text Events
public class TextEventDemo ... {
TextField textField;
TextArea textArea;
TextArea displayArea;
...
//where initialization occurs:
textField = new TextField(20);
...
textField.addTextListener(new MyTextListener("Text Field"));

textArea = new TextArea(5, 20);


textArea.addTextListener(new MyTextListener("Text Area"));
...
}

class MyTextListener implements TextListener {


String preface;

public MyTextListener(String source) {


preface = source
+ " text value changed.\n"
+ " First 10 characters: \"";
}

public void textValueChanged(TextEvent e) {


TextComponent tc = (TextComponent)e.getSource();
String s = tc.getText();
...//truncate s to 10 characters...

displayArea.append(preface + s + "\"\n");
...
}
}

9095883.doc 54
Giáo trình Java
...
}
Lớp TextEvent
Mỗi phương thức Text Event có một thông số đơn : đối tượng TextEvent. Lớp
TextEvent định nghĩa một phương thức. Phương thức getSource mà TextEvent
thừa kế từ EventObject, bạn có thể nhận được thành phần Text liên quan đến sự
kiện này và gởi thông điệp cho nó.
Viết một Window Listener
Các sự kiện của Window được phát ra sau khi Window mở, đóng, thu nhỏ, phóng to,
hoạt động và không hoạt động.
Các phương thức, sự kiện của Window
Giao diện WindowListener và lớp mô phỏng tương ứng, WindowAdapter, chứa
các phương thức sau:
void windowOpened(WindowEvent)
được gọi au khi Window được mở lần đầu.
void windowClosing(WindowEvent)
được gọi sau khi người sử dụng đóng Window.
void windowClosed(WindowEvent)
được gọi sau khi Window đóng lại.
void windowIconified(WindowEvent)
void windowDeiconified(WindowEvent)
được gọi sau khi Window phóng to hay thu nhỏ.
void windowActivated(WindowEvent)
void windowDeactivated(WindowEvent)
được gọi sau khi Window hoạt động hay không hoạt động.
Ví dụ về Handling Window Events
public class WindowEventDemo ... implements WindowListener {
...//where initialization occurs:
//Create but don't show window.
window = new Frame("Window Event Window");
window.addWindowListener(this);
window.add("Center",
new Label("The applet listens to this window"
" for window events."));
window.pack();
}

public void windowClosing(WindowEvent e) {


window.setVisible(false);
displayMessage("Window closing", e);
}

public void windowClosed(WindowEvent e) {


displayMessage("Window closed", e);
}

9095883.doc 55
Giáo trình Java

public void windowOpened(WindowEvent e) {


displayMessage("Window opened", e);
}

public void windowIconified(WindowEvent e) {


displayMessage("Window iconified", e);
}

public void windowDeiconified(WindowEvent e) {


displayMessage("Window deiconified", e);
}

public void windowActivated(WindowEvent e) {


displayMessage("Window activated", e);
}

public void windowDeactivated(WindowEvent e) {


displayMessage("Window deactivated", e);
}

void displayMessage(String prefix, WindowEvent e) {


display.append(prefix
+ ": "
+ e.getWindow()
+ newline);
}
...
}
Lớp WindowEvent
Mỗi phương thức Window Event có một thông số đơn: đối tượng WindowEvent.
Lớp WindowEvent định nghĩa một phương thức, getWindow, trả về Window
phát ra sự kiện này.

5. Tổng kết về Listener API


Phần này This section features a quick-reference table that shows each listener,
its adapter class (if any), and its methods.
6. Giải quyết các vấn đề về Event-Handling
If you're having some hard-to-debug problems related to handling events, you
might find the solution here.

9095883.doc 56
Giáo trình Java

Bài 6: Làm việc với đồ hoạ


Trong bài học này, sẽ hướng dẫn bạn cách hiển thị văn bản, các hình, ảnh đơn
giản, sử dụng API làm việc trong JDK 1.1 và 1.2. Ngoài ra còn có ví dụ về việc
tự tạo một thành phần, sử dụng các API để tạo một đường viền hay một icon.
Các kiến thức về hoạt hình sẽ kết thúc bài học này.
1. Tổng quan về đồ hoạ
Giới thiệu tổng quát về những thông tin cần thiết cho việc bắt đầu thực thi việc
vẽ trong các thành phần.
2. Sử dụng đồ hoạ nguyên bản
Trong phần này sẽ hướng dẫn bạn cách vẽ các hình đơn giản và hiển thị text
một cách có hiệu quả. Bao gồm các ví dụ của việc sử dụng các lớp Graphics,
Font, và FontMetrics classes.
3. Sử dụng hình ảnh
Java platform hổ trợ hình ảnh như thế nào sẽ được bàn bạc trong phần này và
cách để nạp và hiển thị hình ảnh.
4. Thực hiện hoạt cảnh (Performing Animation)
Nhiều chương trình thực hiện hoạt cảnh, hoặc là hoạt hình của chú vịt Duke
đang tung tăng bơi lội hay chỉ đơn giản là một hình ảnh chuyển động trên màn
hình. Trong phần này sẽ nói về cách thực hiện hình ảnh động, cách sử dụng đối
tượng Timer để thực hiện hoạt cảnh.
Tạo vòng lặp cho hoạt cảnh với đối tượng Timer(Creating an
Animation Loop with Timer)
Bước quan trọng nhất để tạo một chương trình hoạt hình chính là khởi tạo các
framework một cách chính xác. Ngoại trừ các hoạt hình thực hiện trực tiếp các
đáp ứng cho các sự kiện mở rộng (ví dụ như việc người dùng kéo một đối
tượng trên màn hình), một chương trình thực hiện hoạt cảnh cần có một vòng
lặp của hoạt hình.
Minh hoạ cho mục này có trong các ví dụ AnimatorAppletTimer.java và
AnimatorApplicationTimer.java. Sau đây là phần tóm lược chung nhất của cả
hai ví dụ. Đây cũng là xườn của một chương trình hoạt cảnh:
public class AnimatorClass ... implements ActionListener
{
int frameNumber = -1;
Timer timer;
boolean frozen = false;

9095883.doc 57
Giáo trình Java
JLabel label;

//In initialization code:


//From user-specified frames-per-second value,
determine
//how long to delay between frames.
...
//Set up a timer that calls this object's action
handler.
timer = new Timer(delay, this);
...
//Set up the components in the GUI.

public synchronized void startAnimation() {


...
timer.start();
...
}

public synchronized void stopAnimation() {


...
timer.stop();
...
}

public void actionPerformed(ActionEvent e) {


//Advance the animation frame.
frameNumber++;

//Request that the frame be painted.


label.setText("Frame " + frameNumber);
}
...
//When the application's GUI appears:
startAnimation();
...
}
Tạo chuyển động cho một hình ảnh trên màn hình (Moving an
Image Across the Screen)
Cách đơn giản nhất để tạo hoạt cảnh là di chuyển một hình ảnh trên màn hình.
Trong thế giới của hoạt cảnh truyền thống, điều này được gọi là cutout
animation.
Có hai hình mà applet sử dụng.

9095883.doc 58
Giáo trình Java
rocketship.gif:

starfield.gif:

Và đây là giao diện của applet. Cần lưu ý là để khởi động hay dừng applet thì
click chuột lên applet.

This is a picture of the applet's GUI. To run the applet, click the picture. The
applet will appear in a new browser window.

Lưu ý: hình rocketship có ảnh nền là transparent.


Đoạn mã để thực hiện hoạt cảnh này không mấy phức tạp. Nói chung, nó cũng
giống như xườn đưa ra ở bên trên. Thay vì sử dụng một label để thực hiện hoạt
cảnh thì nó sử dụng một thành phần tùy biến. Thành phần tùy biến ở đây là lớp
con của JPanel nhằm thực thi việc vẽ cả hai hình ảnh ở trên:
...//Where the images are initialized:
Image background = getImage(getCodeBase(),
"images/rocketship.gif");
Image foreground = getImage(getCodeBase(),
"images/starfield.gif");
...
public void paintComponent(Graphics g) {
super.paintComponent(g); //paint any space not
covered
//by the background image
int compWidth = getWidth();
int compHeight = getHeight();

//If we have a valid width and height for the


//background image, paint it.
imageWidth = background.getWidth(this);
imageHeight = background.getHeight(this);
if ((imageWidth > 0) && (imageHeight > 0)) {
g.drawImage(background,

9095883.doc 59
Giáo trình Java
(compWidth - imageWidth)/2,
(compHeight - imageHeight)/2, this);
}

//If we have a valid width and height for the


//foreground image, paint it.
imageWidth = foreground.getWidth(this);
imageHeight = foreground.getHeight(this);
if ((imageWidth > 0) && (imageHeight > 0)) {
g.drawImage(foreground,
((frameNumber*5)
% (imageWidth + compWidth))
- imageWidth,
(compHeight - imageHeight)/2,
this);
}
}
Có thể bạn sẽ cho rằng việc xoá ảnh nền là không cần thiết khi sử dụng một
ảnh nền nào đó. Tuy nhiên, việc xoá hình nền ở đây vẫn được quan tâm, bởi lẽ
applet luôn luôn khởi động việc vẽ trước khi hình được nạp đầy đủ. Nếu hình
rocketship được nạp trước hình nền thì ta sẽ thấy những phần khác nhau này
của chương trình.
Hiển thi tuần tự các hình ảnh (Displaying a Sequence of
Images)
Trong ví dụ của phần này sẽ cung cấp những bước cơ bản của việc hiển thị
tuần tự các hình ảnh để nó thật giống như hoạt cảnh mà ta thường thấy. Dưới
đây là 10 hình ảnh mà applet sẽ sử dụng:

T1.gif: T2.gif: T3.gif: T4.gif: T5.gif:

T6.gif: T7.gif: T8.gif: T9.gif: T10.gif:

Mã của ví dụ này có trong tập tin ImageSequenceTimer.java, ví dụ này đơn
giản hơn ví dụ vừa mô tả ở trên, chỉ đơn giản là tạo một vòng lặp để hiển thị

9095883.doc 60
Giáo trình Java
thứ tự hết hình này đến hình kia thay vì di chuyển một hình ảnh. Dưới đây là
sự khác biệt đó:
. . .//In initialization code:
Image[] images = new Image[10];
for (int i = 1; i <= 10; i++) {
images[i-1] = getImage(getCodeBase(),
"images/duke/T"+i+".gif");
}

. . .//In the paintComponent method:


g.drawImage(images[ImageSequenceTimer.frameNumbe
r % 10],
0, 0, this);
Cách khác để thực hiện ví dụ này là dùng một label để hiển thị các hình ảnh.
Thay vì sử dụng đoạn lệnh để vẽ lại hình thì ta dùng phương thức setIcon để
thay đổi hình được hiển thị.
Cải tiến giao diện và thực hiện hoạt cảnh (Improving the
Appearance and Performance of Image Animation)
Lưu ý hai việc trong vấn đề hoạt cảnh ở trên:
• Trong khi một bức ảnh đang được nạp, chương trình sẽ hiển thị một
phần của toàn bộ bức ảnh, các phần khác có thể chưa được hiển thị.
• Nạp một bức ảnh sẽ cần một thời gian tương đối dài.
Sử dụng lớp MediaTracker có thể giải quyết được vấn đề về hiển thị
hình ảnh. MediaTracker còn có thể giảm thiểu lượng thời gian để nạp
hình ảnh. Cách khác để cải tiến thời gian nạp hình là thay đổi dạng thức
của tập tin ảnh. Trong phần này sẽ đề cập tới vấn đề này.
Sử dụng MediaTracker để nạp và nạp hình ảnh (Using
MediaTracker to Download Images and Delay Image Display)
Lớp MediaTracker cho phép nạp dữ liệu của một nhóm các tập tin ảnh và kết
thúc khi hình ảnh đã được nạp đầy đủ. Nói chung, dữ liệu của một hình ảnh
chưa được tải về khi nó được vẽ trong lần đầu tiên. Để yêu cầu dữ liệu của các
hình ảnh được chuẩn bị trước để tải về, ta có thể sử dụng các phương thức của
MediaTracker như sau: checkID(anInt, true) hoặc checkAll(true). Để
nạp dữ liệu về một cách đồng bộ, sử dụng phương thức waitForID hoặc
waitForAll. Phương thức MediaTracker sử dụng tiến trình của hệ thống để
tải dữ liệu về, do đó có thể tăng tốc độ của đường truyền.
Để kiểm tra trạng thái của việc nạp dữ liệu về, ta dùng phương thức
MediaTracker statusID hoặc statusAll. Cách đơn giản nhất để kiểm tra xem dữ
liệu của hình ảnh có đang được tải về hay không thì dùng phương thức
checkID hoặc checkAll.

9095883.doc 61
Giáo trình Java
Chương trình MTImageSequenceTimer.java là một ví dụ về việc sử dụng
phương thức MediaTracker waitForAll và checkAll. Applet vẫn hiển thị dòng
chữ "Please wait..." cho đến khi tất cả các hình ảnh đều được nạp đầy đủ.
Những thay đổi về mã dưới đây sử dụng MediaTracker để hiển thị hình ảnh.
Những sự khác nhau được in đậm.
...//Where instance variables are declared:
MediaTracker tracker;

tracker = new MediaTracker(this);

...//In the init method:


for (int i = 1; i <= 10; i++) {
images[i-1] = getImage(getCodeBase(),
"images/duke/T"+i+".gif");
}

...//In the buildUI method,


//which is called by init and main,
//allowing us to run the sample
//as an applet or an application:
for (int i = 1; i <= 10; i++) {
tracker.addImage(images[i-1], 0);
}

...//At the beginning of the actionPerformed method:


try {
//Start downloading the images. Wait until
they're loaded.
tracker.waitForAll();
} catch (InterruptedException e) {}

...//In the paintComponent method:


//If not all the images are loaded, just clear the
background
//and display a status string.
if (!tracker.checkAll()) {
g.clearRect(0, 0, d.width, d.height);
g.drawString("Please wait...", 0, d.height/2);
}

//If all images are loaded, paint.


else {
...//same code as before...
}

9095883.doc 62
Giáo trình Java
Tăng tốc việc nạp hình ảnh (Speeding Up Image Loading)
Cho dù có hay không sử dụng MediaTracker, việc nạp hình ảnh sử dụng
URLs (cách các applets thờng làm) luôn luôn tốn nhiều thời gian. Hầu hết thời
gian ấy là để khởi tạo sự kết nối HTTP. Mỗi một tập tin hình ảnh đòi hỏi một
kết nối HTTP khác nhau, và mỗi một kết nối ấy có thể tiêu tốn vài giây để khởi
tạo. Cho nên, thời gian kéo dài là chuyện đương nhiên.
Cách thức để tránh xãy ra phiền phức trên là nên đặt tất cả các hình ảnh vào
trong một tập tin ảnh. Có thể sử dụng tập tin JAR để thực hiện điều này.
5. Giải quyết các vấn đề về đồ hoạ
Mô tả một vài vấn đề liên quan đến đồ hoạ, giải pháp để giải quyết các vấn đề
này.

9095883.doc 63
Giáo trình Java
Bài 7: Chuyển đổi qua Swing
Trong bài này sẽ trình bày cách thức để chuyển đổi một chương trình từ AWT
sang sử dụng các thành phần Swing. Nếu một chương trình được viết để sử
dụng với JDK 1.0, nghĩa là thay vì sử dụng hệ thống các sự kiện listening được
giới thiệu trong JDK 1.1 thì lại sử dụng các phương thức như là
handleEvent và action, lúc đó, điều trước tiên là chuyển đổi chương trình
để sử dụng hệ thống các sự kiện mới hơn.
1. Tại sao phải chuyển đối
Các thành phần Swing cung cấp cho các nhà lập trình và người dùng đầu cuối
nhiều thuận tiện. Trừ khi bạn có một lý do nào đó thật chính đáng, còn lại thì
tất cả nên chuyển đổi sang sử dụng các thành phần Swing.
The strongest reason to convert to Swing is because it offers many benefits to
programmers and end users. Among them:
The rich set of ready-made components means that you can easily add some
snazzy features to your programs -- image buttons, tool bars, tabbed panes,
HTML display, images in menu items, color choosers, ... The list goes on and
on.
You might be able to replace or reimplement some custom components with
more reliable, extensible Swing components.
Having separate data and state models makes the Swing components highly
customizable, and enables sharing data between components.
Swing's Pluggable Look & Feel architecture gives you a wide choice of look-
and-feel options. Besides the usual platform-specific looks and feels, you can
also use the Java Look & Feel, add an accessory look and feel (such as an
audio "look and feel"), or use a third-party look and feel.
Swing components have built-in support for accessibility, which makes your
programs automatically usable with assistive technologies.
The Swing components will continue to be enhanced in the future.
It's reasonable to put off converting if you don't think your users will be able
run Swing programs conveniently. For example, if your program is an applet
and you want anyone on the Internet to be able to use it, then you have to
consider how many Web surfers have browsers that can run Swing programs.
As of this writing, the major browsers don't have Swing support built in; the
user must add it by downloading and installing Java Plug-in .
You have the choice of upgrading to Java 2 (JDK 1.2) when you convert to
Swing. However, you don't need to decide right now whether to upgrade.
Programs written with JDK 1.1 and Swing generally work just fine in Java 2.

9095883.doc 64
Giáo trình Java
For information about new and improved features of Java 2, see Tables of JDK
Features .
2. Chuyển đổi như thế nào?
Trong phần này sẽ phác thảo về trình tự các bước của việc chuyển đổi chương
trình của bạn sang dùng các thành phần Swing. Việc chuyển đổi này được áp
dụng giống nhau cho cả ứng dụng và applets, trừ phi có những lưu ý khác.
Hoàn toàn tốt đẹp khi thực hiện việc chuyển đổi một chương trình AWT sử
dụng JDK 1.1.
Sau đây là trình tự các bước:
Bước 1: Lưu dự phòng một bản copy của chương trình sẽ chuẩn bị chuyển đổi.
Copy tất cả các tập tin của chương trình, kể cả tập tin .java và .class. Những
bản copy này sẽ hữu dụng cho chung sta trong một số trường hợp sau:
- Cần tham chiếu đến phần mã nguồn trong quá trình chuyển đổi diễn ra.
- Sau khi chuyển đổi, có thể chạy cả hai bản để có thể so sánh giữa chúng với
nhau.
- Một vài chương trình người dùng có thể chưa sẵn sàng nâng cấp VM lên
phiên bản có hỗ trợ lớp Swing. Do đó, có thể là tạm thời sử dụng chương trình
với AWT.
Bước 2: Xoá bỏ tất cả những tham chiếu đến gói java.awt.
Trong bước này, trình biên dịch sẽ làm việc cho bạn. Để thực hiện việc này,
cần vô hiệu tất cả các dòng import hoặc các đoạn lệnh liên quan đến gói
java.awt, trình bêin dịch sẽ nhắc nhở mỗi khi ta dùng một vài lớp nàp đó của
gói AWT. Tuy nhiên, vẫn có một vài lớp thuộc gói AWT vẫn dùng được, ví dụ
như layout managers, nhưng tốt nhất thì chương trình của bạn không nên dùng
các thành phần của AWT. Sau đây là một đoạn lệnh cần được xoá bỏ:
//import java.awt.*; //temporarily remove this import
or
//import java.awt.Button; //temporarily remove this import
import java.awt.Color; //it's OK to leave this in, since
//Color isn't a component
...
/*java.awt.*/Frame = new /*java.awt.*/Frame(...);

9095883.doc 65
Giáo trình Java
Trường hợp không có tham chiếu đến gói AWT, tình biên dịch sẽ phát một lỗi
"not found" khi phát hiện trong chương trình có sử dụng các thành phần AWT.
Điều này giúp cho việc chuyển đổi dẽ dàng hơn là phải ngồi dò từng dòng lệnh
trong cái rừng mà bạn vừa mới tạo ra. Lúc đó, chỉ việc thay đổi các thành phần
AWT sang các thành phần Swing một cách dễ dàng. Trong bước thứ 9, ta sẽ
tìm hiểu cách thêm vào lại các lớp thuộc gói AWT nếu cần.
Step 3: Nếu chương trình là một applet, gở bỏ dòng lệnh import java.applet.*
(nếu có) và bất kỳ tham chiếu nào liên quan đến java.applet.Applet.
Ta không cần tham chiếu đến lớp Applet vì Swing applet đã trở thành một lớp
con của lớp JApplet của Swing. Nếu chương trình đang sử dụng
AppletContext, AppletStub, hoặc AudioClip thì cần phải giữ lại những lớp này
trong gói java.applet. Sau đây là một ví dụ:
//import java.applet.*; //temporarily remove this import
import java.applet.AppletContext; //add this if necessary
Bước 4: Import the gói Swing.
Thêm dong fllẹnh sau đay vào chương trình.
import javax.swing.*;
Dòng lệnh sẽ imports các thành phần Swing cùng với một số lớp Swing khác.
Bước 5: Kiểm nhận một tiến trình an toàn!
Trước khi tiếp tục, cần lưu ý rằng: Although AWT is thread-safe, Swing is not.
Hầu hết các chương trình đều định nghĩa các thành phần trong các phương thức
event-handling và painting, là nhưng phương thức được gọi từ tiến trình event-
dispatching. Định nghĩa một thành phần trong nhưng phương thức là an toàn.
Tuy nhiên, nếu chương trình cần định nghĩa một thành phần ở bất cứ nơi nào
khác nữa, ví dụ như trong một tiến trình chính sau khi GUI đã hiển thị, hoặc
trong mã lệnh, thì nhất thiết phải làm cho nó là thread-safe.
Bước 6: Thay đổi mỗi thành phần AWT thành những thành phần Swing tương
ứng.
Trong nhiều trường hợp, các thành phần AWT và các thành phần Swing tương
ứng đều tương thích về mã lệnh, chỉ cần thay đổi rất đơn giản về tên gọi của
các thành phần mà thôi. Ví dụ như để thay đổi môt AWT button sang một
Swing button, chỉ cần thực hiện như trong bảng dưới đây:
AWT Code:
Button button = new Button("A
Button");

9095883.doc 66
Giáo trình Java

button.addActionListener(this);
Swing Code:
JButton button = new JButton("A
Button");
button.addActionListener(this);
Một vài thành phần Swing có mã nguồn không tương thích với thành phần
AWT tương ứng. Do đó, có một vài thành phần AWT phải viết lại mã lệnh khi
chuyển sang sử dụng các thành phần Swing.
Bước 7: Chuyển đổi cách gọi phương thức add và setLayout.
Chương trình sử dụng các thành phần AWT, ta có thể thêm trực tiếp các thành
phần AWT vào trong frames, dialogs, và applets. Tương tự, ta cũng có thể xác
lập các layout manager trực tiếp vào các đối tượng chứa trên. Trong trường hợp
ngược lại, khi sử dụng các phiên bản Swing có chứa các đối tượng trên, khi
thêm các thành phần thì gọi content pane. Các ví dụ đơn giản sau:
AWT Code:
frame.add(panel, BorderLayout.CENTER);
Swing Code:
frame.getContentPane().add(panel,
BorderLayout.CENTER);
Và đây là phần chi tiết:
AWT Code:
frame.setLayout(new FlowLayout());
frame.add(button);
frame.add(label);
frame.add(textField);
Swing Code:
Container contentPane =
frame.getContentPane();
contentPane.setLayout(new FlowLayout());
contentPane.add(button);
contentPane.add(label);
contentPane.add(textField);
Đoạn mã sau minh hoạ cách chuyển đổi mã của một applet. Chú ý rằng layout
manager mặc định của một content pane là BorderLayout chứ không phải
FlowLayout được sử dụng trong Applet.

9095883.doc 67
Giáo trình Java

AWT Code:
setLayout(new BorderLayout());
add(button, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
Swing Code:
Container contentPane = getContentPane();
contentPane.add(button,
BorderLayout.NORTH);
contentPane.add(panel,
BorderLayout.CENTER);
Bước 8: Di chuyển mã lệnh của việc vẽ ra khỏi các phương thức paint và
update.
Với các thành phần Swing, việc thực hiện painting trong mã lênh được thể hiện
trong phương thức paintComponent. Cách khác để thực hiện painting là sử
dụng các icon chuẩn hoặc tự tạo và đường viền.
Nếu trong chương trình có các lớp con Frame, Dialog, hoặc Applet thực thi
việc làm tươi hay vẽ thì cần phải di chuyển các mã lệnh vào trong một thành
phần khác. Còn việc xác định chính xác thành phần nào thì lại phụ thuộc kiểu
của painting. Nếu một đối tượng chứa có thể thực hiện một icon thì có thể thay
thế thành phần đó bằng một label. Ngoài ra, các thành phần khác có thể thay
thế bởi các lớp con của lớp Jpanel. Có thể thêm các thành phần vào các content
pane của frame, dialog, hoặc applet như đã mô tả ở bước 7.
Bước 9: Sử dụng trình biên dịch để tìm kiếm các thay đổi cần thiết khác.
Sau khi đã phần mã nguồn như đã mô tả ở các bước trên, nên sử dụng trình
biên dịch để vừa biên dịch vừa kiểm tra lại chương trình. Không nên sử dụng
trình biên dịch trong lần đầu đối với chương trình của mình mà chưa có sự
chỉnh sửa nào cả.
Trình biên dịch giúp chúng ta các công việc như sau:
Tìm kiếm các thành phần AWT mà chúng ta đã bỏ sót trong quá trình chuyển
đổi sang Swing. Nếu như ta đã xoá tất cả các dòng lệnh tham chiếu đến
java.awt như ở bước 2 thì trình biên dịch sẽ xuất hiện câu thông báo lỗi như
sau:
TextEventDemo.java:23: Class Button not found
Trong phần khai báo.
Button button = new Button("Clear");
^
9095883.doc 68
Giáo trình Java
Đến thời điểm này, viêc nhận dạng các lớp AWT trong chương trình vẫn còn
cần thiết. Nếu như ta đã xoá tất cả các dòng lệnh tham chiếu đến java.awt, trình
biên dịch hiển thị thông báo như sau:
TextEventDemo.java:17: Class BorderLayout not found
Trong phần khai báo.
BorderLayout layout = new BorderLayout();
^
Các lớp AWT có thể vẫn còn được sử dụng bởi Swing trong chương trình.
Bước 10: Chạy chương trình Swing.
Thực hiện giống như đã mô tả trong phần biên dịch và chạy chương trình
Swing. Chương trình sẽ phát ra lỗi trường hợp chúng ta không nhớ thêm vào
hay setLayout, các lỗi như sau:
java.lang.Error: Do not use javax.swing.JFrame.add() use
javax.swing.JFrame.getContentPane().add() instead
at javax.swing.JFrame.createRootPaneException(JFrame.java:333)
at javax.swing.JFrame.addImpl(JFrame.java:355)
at java.awt.Container.add(Container.java:212)
at AppletDemo.main(AppletDemo.java:121)
Quay trở lại phần mã nguồn và điều chỉnh các phần cho phù hợp.
Bước11: So sánh hai chương trình Swing và AWT, cải tiến chương trình theo
khả năng của Swing.
Mặc dù các phiên bản của Swing và AWT là giống nhau, nhưng việc cải tiến
triệt để theo Swing là cần thiết. Cần lưu ý là có một khác biệt, trừ khi bạn thực
hiện việc copy bản JDK với tập tin swing.properties đã được xác lập, còn lại
thì hầu hết chương trình đều sử dụng một look and feel mới: the Java Look &
Feel. Ta có thể xác lập một look and feel khác nếu muốn.
Step 12: Clean up!
Bây giờ là lúc tinh gọn phần mã lệnh.

9095883.doc 69

You might also like