利用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。