国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

java指南之使用圖形:動(dòng)畫

2019-11-18 14:15:39
字體:
供稿:網(wǎng)友

  執(zhí)行動(dòng)畫
所有形式的動(dòng)畫的一個(gè)共通點(diǎn)就是它們通過以相對而言較快的速度顯示連續(xù)的畫面創(chuàng)造某種能被覺察的運(yùn)動(dòng)效果。計(jì)算機(jī)動(dòng)畫通常每秒顯示10-20幀。比較而言,傳統(tǒng)的手工繪制的動(dòng)畫每秒為8幀(低質(zhì)的動(dòng)畫)到12幀(標(biāo)準(zhǔn)動(dòng)畫)以至到24幀(平滑效果,現(xiàn)實(shí)中的運(yùn)動(dòng)效果)。下面的幾頁將告訴你在java中執(zhí)行動(dòng)畫所需要知道的所有知識。


--------------------------------------------------------------------------------
開始前: 檢查是否有動(dòng)畫工具和諸如 Animator這樣的applet,看看你是否可以用它們來代替自己寫程序。
--------------------------------------------------------------------------------

使用Timer創(chuàng)建循環(huán)的動(dòng)畫
創(chuàng)建一個(gè)動(dòng)畫程序的最重要的一步是正確的制訂計(jì)劃。除了動(dòng)畫僅僅直接反應(yīng)外部事件(例如用戶拖動(dòng)屏幕上的對象)外,程序需要一個(gè)動(dòng)畫循環(huán)。
動(dòng)畫循環(huán)有責(zé)任跟蹤當(dāng)前幀并且周期性的請求屏幕更新。對于applet和很多應(yīng)用程序,你需要一個(gè)單獨(dú)的線程運(yùn)行動(dòng)畫循環(huán)。這一節(jié)包含一個(gè)范例applet和一個(gè)范例程序,它們都使用Timer 對象實(shí)現(xiàn)動(dòng)畫循環(huán)。你可以將這些例子作為你自己的動(dòng)畫的模板

在屏幕上移動(dòng)圖像
最簡單的動(dòng)畫形式是在屏幕上移動(dòng)一個(gè)固定的圖像。在傳統(tǒng)的動(dòng)畫世界中,這被稱為剪紙動(dòng)畫(cutout animation),因?yàn)樗ǔJ峭ㄟ^剪切外形并在鏡頭前移動(dòng)達(dá)到的。
顯示一列圖像
這一節(jié)告訴你如何執(zhí)行經(jīng)典的卡通式的動(dòng)畫:顯示一列圖像。
增強(qiáng)外觀和提高動(dòng)畫性能。
這一節(jié)告訴你如何使用MediaTracker,它可以使你直到動(dòng)畫的所有的圖像都被加載后才顯示動(dòng)畫。你也會(huì)得到一些通過合并圖像文件和使用壓縮方案的方法提高動(dòng)畫性能的提示。

使用Timer創(chuàng)建循環(huán)的動(dòng)畫
通過以規(guī)則的間隔執(zhí)行動(dòng)畫的每個(gè)程序需要一個(gè)動(dòng)畫循環(huán)。通常,這個(gè)循環(huán)應(yīng)該在自己的線程中。它永遠(yuǎn)不應(yīng)該在 paintComponent 方法中,因?yàn)樗鼘⒔庸苁录峙删€程,該線程掌管繪圖和事件處理。
Timer類使得實(shí)現(xiàn)一個(gè)動(dòng)畫循環(huán)很輕易。這一節(jié)兩個(gè)基于Timer的動(dòng)畫模板,一個(gè)用于applet,另一個(gè)用于應(yīng)用程序。applet版本的照片在下面在運(yùn)行該applet時(shí),你可以單擊它停止動(dòng)畫,再次單擊可以讓它繼續(xù)。



這個(gè)圖片是該applet的GUI。要運(yùn)行那個(gè)applet,單擊圖片。該applet將在一個(gè)新瀏覽窗口顯示。

這個(gè)模板動(dòng)畫的行為有點(diǎn)讓人厭煩:它簡單的顯示當(dāng)前的幀數(shù),使用缺省的每秒10幀的速度。下面的幾節(jié)建立這個(gè)范例,向你展示如何制作圖像動(dòng)畫。
你可以從 AnimatorAppletTimer.java得到applet版本模板的代碼。應(yīng)用程序版本模板的代碼在 AnimatorapplicationTimer.java中。這一節(jié)的余下的部分解釋模板中共同的代碼.下面是兩個(gè)模板所做的事情的概覽:

public class AnimatorClass ... implements ActionListener {
int frameNumber = -1;
Timer timer;
boolean frozen = false;
JLabel label;

//初始化:
//從用戶指定的幀/秒值決定幀間的間隔時(shí)間。
...
//設(shè)置一個(gè)定時(shí)器調(diào)用這個(gè)對象的事件處理器。
timer = new Timer(delay, this);
...
//設(shè)置GUI的組件

public synchronized void startAnimation() {
...
timer.start();
...
}

public synchronized void stopAnimation() {
...
timer.stop();
...
}

public void actionPerformed(ActionEvent e) {
//增加動(dòng)畫幀。
frameNumber++;

//請求重繪幀
label.setText("Frame " + frameNumber);
}
...
//當(dāng)程序的GUI顯示時(shí):
startAnimation();
...
}

初始化實(shí)例變量
模板使用了四個(gè)實(shí)例變量。第一個(gè) (frameNumber)表示當(dāng)前幀。它被初始化為-1,即使是第一幀數(shù)是0。原因是幀數(shù)在動(dòng)畫循環(huán)的開始就被增加,這個(gè)時(shí)間先于第一幀被繪制的時(shí)間。因此,第一次被繪制的幀數(shù)是0。
第二個(gè)實(shí)例變量(timer)是一個(gè)Timer對象,它實(shí)現(xiàn)動(dòng)畫循環(huán)。它被初始化為每隔delay毫妙執(zhí)行一次事件。

變量delay是一個(gè)局部變量,由用戶提供的幀/秒 數(shù)初始化。下面的代碼將該值轉(zhuǎn)換為幀間的毫秒數(shù):

delay = (fps > 0) ? (1000 / fps) : 100;

前面代碼中的? : 符號是 if else 的簡短形式。假如用戶提供一個(gè)大于0的幀/秒值,那么間隔就是1000毫秒除以該值。否則間隔就是100毫秒(10幀/秒)。

第三個(gè)實(shí)例變量是 (frozen)是一個(gè)boolean 值,它被初始化為false。當(dāng)用戶請求停止動(dòng)畫,模板將它設(shè)置為 true 。在這一節(jié)的后面你會(huì)看到有關(guān)它的更多信息。

第四個(gè)實(shí)例變量(label)是執(zhí)行繪圖的組件的一個(gè)引用。

動(dòng)畫循環(huán)
Timer 對象通過每delay毫秒觸發(fā)一次事件實(shí)現(xiàn)動(dòng)畫循環(huán)。對應(yīng)于每次事件,actionPerformed 方法執(zhí)行下面的功能:
增加幀數(shù)
請求繪制當(dāng)前幀。
要獲得更多有關(guān)定時(shí)器的信息,請參考 如何使用定時(shí)器。

禮貌行為
動(dòng)畫模板有兩個(gè)出于禮貌的特性。
第一個(gè)是運(yùn)行用戶明確的停止(和重新啟動(dòng))動(dòng)畫,同時(shí)applet或者程序維持可見。動(dòng)畫可能會(huì)使人心煩意亂,因此給予用戶停止動(dòng)畫而將注重力集中在其它的事情上的能力是個(gè)不錯(cuò)的主意。這個(gè)特性是通過重寫 mousePRessed 方法實(shí)現(xiàn)的,它根據(jù)程序的當(dāng)前狀態(tài)決定是停止還是重新啟動(dòng)定時(shí)器。下面是實(shí)現(xiàn)的代碼:

...//初始化代碼中:
boolean frozen = false;
...
public synchronized void startAnimation() {
if (frozen) {
//什么也不做。用戶請求停止圖像變化。
} else {
//開始動(dòng)畫!
...
timer.start();
...
}

public synchronized void stopAnimation() {
...
timer.stop();
...
}
...
//動(dòng)畫組件的鼠標(biāo)監(jiān)聽器中:
public void mousePressed(MouseEvent e) {
if (frozen) {
frozen = false;
startAnimation();
} else {
frozen = true;
stopAnimation();
}
}

第二個(gè)特性是在applet或者程序不可見的時(shí)候掛起動(dòng)畫。對于 applet模板,這是通過在stop 和 start 方法中分別調(diào)用stopAnimation 和startAnimation方法達(dá)到的。對于程序模板,這是通過實(shí)現(xiàn)窗口事件處理器達(dá)到的,該處理器重定義最小化和恢復(fù),在其中再次分別調(diào)用stopAnimation 和startAnimation 。

在兩個(gè)模板中,假如用戶沒有停止動(dòng)畫,那么當(dāng)程序檢測到動(dòng)畫不可見時(shí),它通知定時(shí)器停止。當(dāng)用戶再次訪問動(dòng)畫,程序重新啟動(dòng)動(dòng)畫,除非用戶明確請求停止動(dòng)畫。

Moving an Image Across the Screen
This page features an example applet that moves one image (a rocketship) in front of a background image (a field of stars). You could implement this in one of two ways -- either using one label per image, or using one custom component that paints both images. Because this lesson features painting, this section features the custom component approach, as implemented in MovingImageTimer.java.

--------------------------------------------------------------------------------
Note: You can also see an alternate implementation, which uses labels and a layered pane. You can find it in MovingLabels.java, which you can run by visiting MovingLabels.Html.
--------------------------------------------------------------------------------

Below are the two images this applet uses.

rocketship.gif:




starfield.gif:




Here´s a picture of the applet´s GUI. Remember that you can click on the applet to stop or start the animation.



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.


--------------------------------------------------------------------------------
Note: The rocketship image has a transparent background. The transparent background makes the rocketship image appear to have a rocketship shape, no matter what color background it´s painted on top of. If the rocketship background weren´t transparent, then instead of the illusion of a rocketship moving through space, you´d see a rocketship on top of a rectangle moving through space.
--------------------------------------------------------------------------------


The code for performing this animation isn´t complex. Essentially, it´s a copy of the animation template that, instead of using a label to perform animation, uses a custom component. The custom component is a JPanel subclass that paints two images, one of which has a position that depends on the current frame number. Here is the code that paints the custom component:

...//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);
}
}

You might think that this program doesn´t need to clear the background, since it uses a background image. However, clearing the background is still necessary. One reason is that the applet usually starts painting before the images are fully loaded. If the rocketship image loaded before the background image, you would see parts of multiple rocketship until the background image loaded. Another reason is that if the applet painting area were wider than the background image, for some reason, then you´d see multiple rocketships to either side of the background image.
You could solve the first problem by delaying all painting until both images are fully loaded. The second problem could be solved by scaling the background image to fit the entire applet area. You´ll learn how to wait for images to be fully loaded in Improving the Appearance and Performance of Image Animation, later in this lesson. Scaling is described in Displaying Images.

顯示一列圖像
這一節(jié)的范例給出顯示一列圖像的基礎(chǔ),下一節(jié)有關(guān)于增強(qiáng)外觀和動(dòng)畫性能的提示。這一節(jié)只顯示applet代碼,對于應(yīng)用程序的代碼很相似,除了應(yīng)該使用加載圖像 中描述的方法加載圖像。
下面是這個(gè)applet使用的十個(gè)圖像。

T1.gif: T2.gif: T3.gif: T4.gif: T5.gif:
T6.gif: T7.gif: T8.gif: T9.gif: T10.gif:

下面是applet的圖片。記住你可以點(diǎn)擊applet讓它停止和重新開始動(dòng)畫。



這個(gè)圖片是該applet的GUI。要運(yùn)行那個(gè)applet,單擊圖片。該applet將在一個(gè)新瀏覽窗口顯示。

這個(gè)范例的代碼在 ImageSequenceTimer.java中,它甚至比前一個(gè)移動(dòng)圖像的范例的代碼還簡單。下面是和移動(dòng)圖像的范例的代碼明顯不同的地方:

. . .//在初始化代碼中:
Image[] images = new Image[10];
for (int i = 1; i <= 10; i++) {
images[i-1] = getImage(getCodeBase(), "images/duke/T"+i+".gif");
}

. . .//在paintComponent方法中:
g.drawImage(images[ImageSequenceTimer.frameNumber % 10],
0, 0, this);

實(shí)現(xiàn)這個(gè)范例的另一個(gè)方法是使用一標(biāo)簽顯示圖像,不使用自定義繪圖代碼,使用setIcon 方法改變要顯示的圖像。

增強(qiáng)外觀和動(dòng)畫性能
你可能注重到了上一頁的動(dòng)畫的兩個(gè)問題:
在圖像還在加載時(shí),程序部分的顯示某些圖像,有些根本就不顯示。
加載圖像花費(fèi)的時(shí)間太長。
顯示部分圖像的問題很輕易修正,使用 MediaTracker 類。 MediaTracker 也能減少圖像加載的時(shí)間。處理圖像加載緩慢的另一個(gè)方法是以某種方式改變圖像格式;這一頁對此給出一些建議。


--------------------------------------------------------------------------------
注重: ImageIcon 類 在創(chuàng)建時(shí)通過Swing自動(dòng)使用一個(gè)MediaTracker下載圖像數(shù)據(jù)。更多信息請參看 如何使用圖標(biāo)。
--------------------------------------------------------------------------------

使用MediaTracker下載圖像并延遲顯示圖像
MediaTracker 類可以讓你很輕易的下載一組圖像并且得到圖像被全部加載完的時(shí)間。通常,圖像數(shù)據(jù)在第一次顯示時(shí)才被下載,為了請求一組圖像異步地被預(yù)先加載,使用下面的MediaTracker 一個(gè)方法: checkID(anInt, true) 或者 checkAll(true)。同時(shí)加載數(shù)據(jù)(等待數(shù)據(jù)到達(dá))使用 waitForID 或者 waitForAll。 MediaTracker 的數(shù)據(jù)加載方法使用幾個(gè)后臺進(jìn)程下載數(shù)據(jù),這可以加快下載速度。
要檢查圖像加載的狀態(tài),你可以使用MediaTracker的 statusID 或者 statusAll 方法。僅僅想檢查是否有圖像還需要加載,可以使用 checkID 或者checkAll 方法。

MTImageSequenceTimer.java 是使用MediaTracker的 waitForAll 和checkAll 方法的一個(gè)修正版。在所有的圖像完全加載前,這個(gè)applet只是簡單的顯示一個(gè)"Please wait..." 消息。參看 MediaTracker API 文檔 獲得一個(gè)立即繪制一個(gè)背景圖像但是延遲繪制動(dòng)畫圖像的范例。

下面是applet的截圖:



這個(gè)圖片是該applet的GUI。要運(yùn)行那個(gè)applet,單擊圖片。該applet將在一個(gè)新瀏覽窗口顯示。

下面是改變的代碼,它使用一個(gè) MediaTracker 幫助推遲圖像的顯示。不同的地方使用黑體標(biāo)記了。
...//聲明實(shí)例變量:
MediaTracker tracker;

tracker = new MediaTracker(this);

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

...//在buildUI 方法中,
//它被init和main方法調(diào)用,
//運(yùn)答應(yīng)我們將這個(gè)例子作為一個(gè)applet或者應(yīng)用程序運(yùn)行:
for (int i = 1; i <= 10; i++) {
tracker.addImage(images[i-1], 0);
}

...//在actionPerformed 方法的開始:
try {
//開始下載圖像,等待它們被加載完畢。
tracker.waitForAll();
} catch (InterruptedException e) {}

...//在paintComponent方法中:
//假如并非所有的圖像被加載完,清除背景并顯示狀態(tài)字符串。
if (!tracker.checkAll()) {
g.clearRect(0, 0, d.width, d.height);
g.drawString("Please wait...", 0, d.height/2);
}

//假如所有的圖像都被加載,繪制它們。
else {
...//same code as before...
}

加快圖像的加載
無論你是否使用MediaTracker,使用URL加載圖像(就像通常的applet所做的那樣)通常要花費(fèi)一段較長的時(shí)間。這些時(shí)間的大多是初始化HTTP連接占用的,每一個(gè)圖像文件需要一個(gè)單獨(dú)的HTTP連接,而每個(gè)連接可能花費(fèi)幾秒進(jìn)行初始化。
避免這個(gè)性能損失的要害是將所有的圖像放在一個(gè)文件里面。你可以使用一個(gè)JAR 文件做到這個(gè),就像 將Applet的文件合并到一個(gè)文件里面 和 使用 JAR文件: 基礎(chǔ)中描述的那樣。

另一個(gè)可能有用的性能策略是將一組圖像合并到一個(gè)圖像文件中。可以到達(dá)這個(gè)目的的一個(gè)簡單的方法是創(chuàng)建一個(gè)圖像序列 --一個(gè)文件包含一行圖像。下面是一個(gè)圖像序列的例子:

jack.gif:




要從一個(gè)圖像序列中繪制一個(gè)圖像,你首先需要將繪圖區(qū)的大小設(shè)置為一個(gè)圖像的大小。然后繪制圖像序列,(假如必要的話)向左移位,因此在繪圖區(qū)只有你想要顯示的圖像出現(xiàn)。例如:

//imageStrip 是一個(gè)表示圖像序列的Image對象。
//imageWidth 是序列中的一個(gè)單獨(dú)的圖像的大小。
//imageNumber 是要繪制的圖像的序號(從0到numImages)
int stripWidth = imageStrip.getWidth(this);
int stripHeight = imageStrip.getHeight(this);
int imageWidth = stripWidth / numImages;
g.clipRect(0, 0, imageWidth, stripHeight);
g.drawImage(imageStrip, -imageNumber*imageWidth, 0, this);

假如你想圖像加載得更快,你應(yīng)該考慮圖像壓縮方案,非凡是幀內(nèi)壓縮。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阿合奇县| 武隆县| 辉南县| 桂东县| 临城县| 七台河市| 澄江县| 美姑县| 宾阳县| 塘沽区| 宜阳县| 柳江县| 新蔡县| 霍林郭勒市| 仁寿县| 丘北县| 措美县| 永胜县| 阿尔山市| 永兴县| 关岭| 南靖县| 桐梓县| 锦州市| 揭西县| 泰顺县| 塔城市| 淮北市| 秀山| 永春县| 钟祥市| 厦门市| 怀化市| 横峰县| 广南县| 崇仁县| 淳安县| 江永县| 洪泽县| 读书| 饶平县|