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; 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. 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,
(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ị 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.frameNumber % 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.
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) {}
//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... }
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.
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.