Chương 6. LẬP TRÌNH GIAO DIỆN ĐỒ HỌA
6.4. Hiển thị thông tin trong một thành phần
Trong phần này, chúng tôi sẽ chỉ cho bạn cách hiển thị thông tin bên trong một khung. Ví dụ, thay vì hiển thị dòng thông báo “Not a Hello World” dưới chế độ văn bản trong cửa sổ console như chúngta đã làm, chúng tasẽ hiển thị thông điệp trong một khung, như trong hình 6.7.
Hình 6.7. Một khung hiển thị thông tin.
Bạn có thể vẽ/hiển thị một chuồi thông điệp trực tiếp lên khung, nhưng đó không được coi là cách lập trình tốt. Trong Java, các khung được thực sự thiết kế như các bộ chứa cho các thành phần (component), chẳng hạn như một thanh menu và các thành phần giao diện người dùng khác. Bạn thường vẽ/hiển thị trên một thành phần khác mà bạn thêm vào khung.
Cấu trúc của một JFrame phức tạp một cách đáng ngạc nhiên. Nhìn vào Hình 6.8, bạn sẽthấy thiết kế của một khung hình JFrame. Như bạn có thể thấy, bốn bảng con được xếp lóp trong một JFrame. Chúng ta không cần quan tâm đến báng con gốc (root pane), bảng con xếp tầng (layeredpane) và bảng con kính (glass pane). Chúng được yêu cầu để sắp xếp thanh menu, bảng nội dung và để cài đặt giao diện look-and-feel. Phần bảng con mà hầu hết cáclập trình viên Swing quan tâm là bảng nội dung (content pane). Khi thiết kế khung, bạn phải thêm các thành phần vào bảng nội dung, sử dụngđoạn mã như sau:
Container contentPane =frame.getContentPane();
Componentc =...;
contentPane.add(c);
Hình 6.8. Cấu trúc bên trong của Jframe.
Lên đến Java SE 1.4, phương thức add của lớp JFrameđã được định nghĩa để némra một ngoại lệ với thông báo “Không sử dụng JFrame.add(). Thay vào đó hãy sử dụng JFrame.getContentPane().add ”. Ngày nay, phương thức JFrame.add đã được loại bỏ, giúp cố gắng đào tạo lại các lập trình viênđể đơn giản chỉ gọi phương thức add trên bảng nội dưng.
Trong trường họp của chúng ta, chúng ta muốn thêm một thành phần vào khung mà trên đó chúng ta sẽ hiến thị thông điệp. Đe vẽ trên một thành phần, bạn phải định nghĩa một lóp mở rộng của lóp JComponent và ghi đè lên phương thức paỉntComponent trong lớp đó. Phương thức paintComponent nhận một tham số có kiểu Graphics. Một đối tượng Graphics lưu giữ một tập họp các cài đặt đế vẽ hình ảnh và văn bản, chang hạn như phông chữ hoặc màu hiện tại. Tất cả việc vẽ trong Java phải đi qua một đối tượng Graphics. Nó có các phương thức vẽ mẫu, hình ảnh và văn bản.
LƯU Ý: Tham số Graphics tương tự như ngữ cảnh thiết bị trong Windows hoặc ngữ cảnh đồ họa trong lập trìnhXI1.
Sau đây là cách tạo thànhphầnđể chúng ta có thể vẽtrên đó:
class MyComponent extends JComponent { public void paintComponentfGraphics g) {
<Đoạn mãđểvẽ>
} }
Mỗi khi một cửa sổ cần được vẽ lại, với bất kể lý do gì, trình xử lỷ sự kiện sẽ thông báo cho thành phầnđó. Điều này làm cho các phương thức paintComponent của tất cả các thành phần được thực thi. Bạn không được gọi phương thức paintComponent một cách trực tiếp. Nó được gọi tự động bất cứ khi nào một phần củaứng dụng cần được vẽ lại và bạn không nên can thiệp vào quá trìnhtự động này. Vậy những loại hành động nào sẽ kích hoạt phản ứng tự động này? Ví dụ, việc vẽ lại xảy ra khi người dùng tăng kích thước của cửa sổ, hoặc thu nhó và sau đó khôi phục lại cửa so. Neu người dùng bật lên một cửa sổ khác làm che đi cửa sổ hiện tại và sau đó bạn làm cho cửa sổ mới này biến mất, thì cửa sổ bị che, hiện đang không được vẽ, sẽ cầnđược vẽ lại (hệ thống đồ họa không lưu các điểm ảnh bên dưới). Và, tất nhiên, khi cửa sổ được hiểnthị lần đầu tiên, nó cần xử lý đoạn mã để chỉ rõ cách thức vàvị trí cầnvẽ các phần tử ban đầu.
LỜI KHUYÊN: Neu bạn bắt buộc cần vẽ lại màn hình, hãy gọi phương thức repaintthay vì paỉntComponent. Phương thức repaint sẽ khiến chopaỉntComponent được gọi ở tất cả các thành phần, với một đối tượng Graphics được cấu hình đúng.
Như bạn đã thấy trong đoạn mã trên, phương thức paỉntComponent nhận một tham số duy nhất là đối tượng của lóp Graphics. Độ đo được sử dụng trên một đối tượng Graphics khi hiển thị trên màn hình là pixel. Tọa độ (0, 0) biểu thị góc trên cùng bên trái của thànhphần trên bề mặt bạn đang vẽ.
Hiển thị văn bản đượccoi là một kiểu vẽđặc biệt. Lớp Graphics có một phương thức drawString có cú pháp sau: g.drawStrỉng(text, X, y).
Trong trường hợp của chúng ta, chúng ta muốn hiển thị chuỗi “Not a Hello World Program" trong cửa so gốc của chúng ta, với khoảng cách một phần tư từ bên trái và một nửa từ bên trên so với tọa độ (0, 0). Mặc dù chúng ta chưa biết cách đo kích thước của chuỗi, chúng ta sẽbắt đầu chuỗi tại tọa độ (75, 100). Điều này có nghĩa là ký tự đầu tiên trong chuỗi sẽ bắt đầu ở vị trí 75 pixel ở bên phải và 100 pixel ở trên xuống (trên thực tế, đó là đường cơ sở cho văn bản có độ phân giải 100 pixel). Vì vậy, phương thức paintComponent củachúngtatrông như sau:
public class NotHelloWorldComponent extends JComponent {
public static final int MESSAGE-X = 75;
public static final int MESSAGE_Y = 100;
publicvoid paintComponent(Graphics g) {
g.drawStringf'Nota Hello World program", MESSAGE-X, MESSAGE-Y);
}
}__________________________________________________________________
Cuối cùng, một thành phần nên thông báo cho người dùng biết độ lớn mong muốn của nó. Chúng ta sẽ ghi đè phương thức getPreferredSize và trả về đối tượng của lóp Dimension với chiều rộng và chiều cao ưathích:
public class NotHelloWorldComponent extends JComponent {
private static final int DEFAULT-WIDTH = 300;
private static final int DEFAULT-HEIGHT = 200;
public Dimension getPreferredSizeQ {
return new Dimension(DEFAULT_WIDTH, DEFAULT-HEIGHT];
}
}__________________________________________________________________
Khi bạn điền nội dung vào một khung với một hoặc nhiều thành phần và bạn chỉ muốn sử dụng kích thước ưa thích của chúng, hãy gọi phương thức pack thay vì phương thức setSize'.
class NotHelloWorldFrame extends JFrame {
public NotHelloWorldFrameQ {
addfnew NotHelloWorldComponentO);
packQ;
}
}__________________________________________________________________
Chương trình 6.3 là mã nguồn hoàn chỉnh.
Chương trình 6.3 import javax.swing.*; importjava.awt.*;
public class NotHelloWorld {
public static void main(String[] args]
{
EventQueue.invokeLaterfO ->
{
JFrame frame = new NotHelloWorldFrameQ;
frame.setTitlef'NotHelloWorld");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE]
frame.setVisible(true);
B;
} }
// Lớp khung chứa một bảng thôngđiệp class NotHelloWorldFrame extendsJFrame{
public NotHelloWorldFrameQ {
addfnewNotHelloWorldComponentO);
packQ;
} }
// Một thành phần để hiển thịmộtthôngđiệp
class NotHelloWorldComponent extends JComponent { public static final int MESSAGE-X= 75;
public static final int MESSAGE-Y = 100;
private static final int DEFAULT-WIDTH = 300;