利用gif圖片制作簡單動畫是常用的渲染手段,swing雖然支持gif圖片格式並可以自動地實現動畫效果。
通常最簡單地將gif圖片放到swing組件上是調用JButton或JLabel的setIcon(Icon icon)方法。
還有一種方法是重寫paintComponent(Graphics g)或paint(Graphics g)方法。例如
public class ShowGifPanel extends JPanel{
ImageIcon image = new ImageIcon("/root/opt/loading.gif");
@Override
public void paint(Graphics g) {
g.drawImage(image.getImage(), 0, 0, this);
}
}
通過上述方法呈現如下3個gif。
但是事實情況卻是:不要企圖通過這樣簡單的處理達到理想的效果。如果你這樣做的話馬上會發現gif的刷新率往往非常快,看上去gif圖片桢刷新很快,或者應該說太快了。
swing還提供了一種實現手段是設置一組相似的gif,通過輪循顯示來呈現,通過下圖就明了了。
這樣雖然可以呈現,但是對於一個動畫來說就必須提供多個gif。對於占用空間和給美工的負擔都不利。
如果你使用SWT呈現Gif,Eclipse提供了一個方案。
http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet141.java?view=co
其基本原理就是將Gif的各個桢輪循地顯示,如果你以這個程序運行loading.gif,看上去還是很快,可以通過修改第131~137行之間的代碼來調節刷新率。這樣SWT就能完美實現處理Gif了。
不幸的是,將SWT的那種方式移植到Swing中卻達不到很好的效果。在Swing中要想完美實現處理Gif需要額外的一些工作。
首先需要對Gif這種圖片格式有一些基本認識。
第一:Gif由一系列Image組成,也就是桢,Gif動畫就是連續地顯示這些桢,但是這還不夠。
第二:無論某一時刻輪循到哪一桢,第1桢,總是要當作背景畫出來,而且第1桢也是所有桢當中最長最高的,它的尺寸也是整個Gif圖象的尺寸,位置從(0, 0)開始,其余各桢可能只是描述與相臨各桢變化的部分,所以長和高要小且不完整,起始位置是該桢相對整體背景的位置。(這點SWT也是這樣做的)
第三:Gif動畫連續顯示不一定是各個桢輪循單獨顯示,而是不僅僅顯示當前該顯示的桢,還要向前追溯到"第一桢",從"第一桢"開始到當前應該顯示的桢組成的連續一系列"桢簇",所以某一時刻單單顯示背景和當前桢是不夠的,而是顯示背景和當前"桢簇"。""桢簇""是我自己取的名字,而且我看SWT輪循的例子中並沒有用到"桢簇",而是傳統的單桢輪循。但是同樣的方法對Swing不奏效,現在我對此還不得其解。關於"第一桢",是和com.sun.imageio.plugins.gif.GIFImageMetadata類的disposalMethod屬性有關,在SWT中這個屬性是org.eclipse.swt.graphics.ImageData.disposalMethod。disposalMethod據我的研究是描述處理桢的方法,常見的disposalMethod取值有none(取值0,不處理)、Background(取值2,背景)兩種,所謂的當前桢的"第一桢"就是向前追溯到最近的disposalMethod取值為2的那一桢的下一桢,也就是說或者"第一桢"的前一桢的disposalMethod取值為2,或者"第一桢"就是Gif索引為2的桢,因為Gif的第1桢總要當背景顯示。
第四:桢的元數據在SWT中用org.eclipse.swt.graphics.ImageData類封裝,在Swing中對應的是com.sun.imageio.plugins.gif.GIFImageMetadata(可是截止到JDK6.0 u11,這個類的版本號還是0.5,有些另人失望:(),可以通過次類獲取到delayTime這個屬性,也就是下一桢的間隔時間,但是有很多Gif,這個值總是0,所以Swing顯示頻率相當的快。
以下是本人寫的2個參考實現,其中GifAnalysis.java是gif的分析工具,它將Gif的各個桢單獨拿出來分析比對,並列出了上面提到的一些屬性。如下圖
通過比較發現loading.gif各個桢的delayTime均為0,因此單純地將loading.gif設置為JLabel等組件的icon屬性效果必定會出問題,可以通過美工解決。
Gif.java是呈現gif的參考,需要留意構造函數public Gif(File gifFile, int delayFactor),第二個參數是延時因子,數值越大每一桢的間隔就越長,對於loading.gif該值調節為105較為合適,而tt1.gif和javafx-loading-100x100.gif這個值應該是10。
本文配套源碼