Eclipse RCP 是没有原生API支持Gif动画播放,需要自己手动实现。
大家都知道Gif动画由多帧image组合而成。所以顺利实现Gif播放就是读取到Gif源文件并将包含的多帧image完全读取出来,并按一定时间间隔播放即可。
** 源代码如下:**
借鉴了 喜来乐哈哈代码,特此声明引用。
/**
* 负责显示各种格式的图片
*
* @author 喜来乐哈哈
*/
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.draw2d.TextUtilities;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Pattern;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
public class ImageViewer extends Composite {
private static final String NO_PICTURE = "No Picture!";
String key;// 为每个gif格式图片做标识的key,一般使用图片名称
String imageKey;// 为每个图片都做内存缓存
Map<String, Image[]> gifCache = new ConcurrentHashMap<>(10);
Map<String, Image> imageCache = new ConcurrentHashMap<>(10);
/**
* 图片堆叠类型
*/
int picType = 0; // 0 居中 1 平铺 2拉伸
protected Point origin = new Point(0, 0);
protected Image image;
protected ImageData[] imageDatas;
protected Image[] images;
protected int current;
private int repeatCount;
private Runnable animationTimer;
private Color bg;
private Display display;
public ImageViewer(Composite parent) {
super(parent, SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.TRANSPARENT);
bg = getBackground();
display = parent.getDisplay();
addListeners();
}
int getPicType() {
return picType;
}
void setPicType(int picType) {
if (this.picType != picType) {
this.picType = picType;
}
}
public void setImage(String imageKey, ImageData imageData) {
this.imageKey = imageKey;
checkWidget();
stopAnimationTimer();
if (Objects.nonNull(imageCache.get(imageKey))) {
this.image = imageCache.get(imageKey);
} else {
this.image = new Image(display, imageData);
if (!imageCache.containsKey(imageKey)) {
imageCache.put(imageKey, image);
System.out.println(imageKey + " put to imageMap cache!!!");
}
}
this.imageDatas = null;
this.images = null;
redraw();
}
/**
* @param repeatCount 0 forever
*/
public void setImages(String key, ImageData[] imageDatas, int repeatCount) {
this.key = key;
checkWidget();
this.current = 0;
this.image = null;
this.imageDatas = imageDatas;
this.repeatCount = repeatCount;
convertImageDatasToImages();
startAnimationTimer();
redraw();
}
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
checkWidget();
Image image = getCurrentImage();
if (image != null) {
Rectangle rect = image.getBounds();
Rectangle trim = computeTrim(0, 0, rect.width, rect.height);
return new Point(trim.width, trim.height);
}
return new Point(wHint, hHint);
}
@Override
public void dispose() {
if (image != null)
image.dispose();
if (images != null)
for (int i = 0; i < images.length; i++)
images[i].dispose();
if (!gifCache.isEmpty()) {
gifCache.clear();
}
if (null != bg)
bg.dispose();
super.dispose();
}
protected void paint(Event e) {
Image image = getCurrentImage();
GC gc = e.gc;
if (image == null) {
// 画个空白文字在图片上
FontData fntData = gc.getFont().getFontData()[0];
String text = new String(NO_PICTURE);
// Label label = new Label(this.getParent(),SWT.NONE);
// int textWidth = Utility.getWidth(text,label);
int textWidth = TextUtilities.INSTANCE.getTextExtents(text, gc.getFont()).width;
// label.dispose();
gc.drawText(NO_PICTURE, getBounds().width / 2 - textWidth / 2,
getBounds().height / 2 - fntData.getHeight() / 2, SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC);
return;
}
int picType = this.getPicType();
/////////////////////////////////////////////////////////////////////
int width = this.getBounds().width;
int height = this.getBounds().height;
int imageWidth = image.getBounds().width;
int imageHeight = image.getBounds().height;
switch (picType) {
case 1: {// 平铺
Pattern ptn = new Pattern(null, image);
gc.setBackgroundPattern(ptn);
gc.fillRectangle(0, 0, width, height);
ptn.dispose();
}
break;
case 2: { // 拉伸
gc.drawImage(image, 0, 0, imageWidth, imageHeight, 0, 0, width, height);
}
break;
case 0:// 居中
{
gc.drawImage(image, imageWidth > width ? (imageWidth - width) / 2 : 0,
imageHeight > height ? (imageHeight - height) / 2 : 0, Math.min(imageWidth, width),
Math.min(imageHeight, height), width > imageWidth ? (width - imageWidth) / 2 : 0,
height > imageHeight ? (height - imageHeight) / 2 : 0, Math.min(imageWidth, width),
Math.min(imageHeight, height));
}
break;
default:
break;
}
}
private void patternDispose(Pattern ptn) {
if (Objects.nonNull(ptn)) {
ptn.dispose();
ptn = null;
}
}
void addListeners() {
addListener(SWT.Paint, new Listener() {
public void handleEvent(Event e) {
paint(e);
}
});
}
void convertImageDatasToImages() {
int length = imageDatas.length;
images = new Image[length];
// Step 1: Determine the size of the resulting images.
int width = imageDatas[0].width;
int height = imageDatas[0].height;
// Step 2: Construct each image.
int transition = SWT.DM_FILL_BACKGROUND;
// 首先从缓存中查找当前key有没有images[]列表,如果有,直接遍历即可。如果没有,重新生成,并将当前image放入cache 中。
if (Objects.nonNull(gifCache.get(key))) {
images = gifCache.get(key);
for (int i = 0; i < length; i++) {
ImageData id = imageDatas[i];
GC gc = new GC(images[i]);
transition = drawImage(width, height, transition, i, id, gc);
}
} else {
if (!gifCache.containsKey(key)) {
this.gifCache.put(key, images);
System.out.println(key + " put to cacheMap");
}
for (int i = 0; i < length; i++) {
ImageData id = imageDatas[i];
images[i] = new Image(display, width, height);
GC gc = new GC(images[i]);
transition = drawImage(width, height, transition, i, id, gc);
}
}
}
private int drawImage(int width, int height, int transition, int i, ImageData id, GC gc) {
// Do the transition from the previous image.
switch (transition) {
case SWT.DM_FILL_NONE:
case SWT.DM_UNSPECIFIED:
// Start from last image.
gc.drawImage(images[i - 1], 0, 0);
break;
case SWT.DM_FILL_PREVIOUS:
// Start from second last image.
gc.drawImage(images[i - 2], 0, 0);
break;
default:
// DM_FILL_BACKGROUND or anything else,
// just fill with default background.
gc.setBackground(bg);
gc.fillRectangle(0, 0, width, height);
break;
}
// Draw the current image and clean up.
Image img = new Image(display, id);
gc.drawImage(img, 0, 0, id.width, id.height, id.x, id.y, id.width, id.height);
img.dispose();
gc.dispose();
// Compute the next transition.
// Special case: Can't do DM_FILL_PREVIOUS on the
// second image since there is no "second last"
// image to use.
transition = id.disposalMethod;
if (i == 0 && transition == SWT.DM_FILL_PREVIOUS)
transition = SWT.DM_FILL_NONE;
return transition;
}
Image getCurrentImage() {
if (image != null)
return image;
if (images == null)
return null;
current = current >= images.length ? (images.length - 1) : current;
return images[current];
}
void startAnimationTimer() {
int length = images.length;
if (images == null || length < 2)
return;
// 将原来的timertask stop dispose
stopAnimationTimer();
final int delay = imageDatas[current].delayTime * 10;
display.timerExec(delay, animationTimer = new Runnable() {
public void run() {
if (isDisposed())
return;
current = (current + 1) % length;
redraw();
if (current + 1 == length && repeatCount != 0 && --repeatCount <= 0)
return;
display.timerExec(delay, this);
}
});
}
void stopAnimationTimer() {
if (animationTimer != null)
display.timerExec(-1, animationTimer);
}}
下面写一个main方法来测试一下是否支持Gif及其他静态图片。
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.ImageLoader;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
public class ImageCanvasTest {
public static void main(String[] args) {
Display display = new Display();
final Shell shell = new Shell(display);
ImageViewer ic = new ImageViewer(shell);
shell.setLayout( new FillLayout());
FileDialog dialog = new FileDialog(shell, SWT.OPEN);
dialog.setText( " Open an image file or cancel " );
String string = dialog.open();
ImageLoader loader = new ImageLoader();
ImageData[] imageDatas = loader.load(string);
if (imageDatas.length == 0 )
return ;
else if (imageDatas.length == 1 ) {
ic.setImage("aabbcc",imageDatas[ 0 ]);
} else {
ic.setImages("aabbcc",imageDatas, loader.repeatCount);
}
ic.pack();
shell.pack();
shell.open();
while ( ! shell.isDisposed()) {
if ( ! display.readAndDispatch())
display.sleep();
}
display.dispose();
}}
代码贴完了,解释一下
ImageViewer类就是实现了读取静态图片和Gif图片的实现类。支持图片的居中,拉伸和平铺等三种展示方式,默认为居中显示。
ImageCanvasTest类实现了打开图片窗口,并使用ImageLoader类来读取图片资源。该类支持Gif格式。会将Gif格式图片读取为ImageData[]数组。
还有一个注意的地方:loader.pepeatcount=0.0为无限次循环,即Gif图片会一直循环播放。如果设定了次数,则只会播放设定的次数。