直接代码,详见注释。
问题: 1.性能方面不是很理想,该实现方式会向目标控件请求redraw,频繁的刷新可能会引起闪屏等问题
建议:a)使用双缓存
b)如果可以的话,在单独的canvas上绘制,这时所有的处理仅针对canvas即可
本想按照b)封装的,但考虑到其灵活性,并没有这样做
2.如果面对大量的GIF图片或者帧间隔时间很小,或许会存在问题。
3.当心BUG,它无处不在
- package cn.schina.dbs.ui.swt2d;
- import java.io.InputStream;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Queue;
- import java.util.concurrent.ConcurrentLinkedQueue;
- import org.eclipse.swt.events.PaintEvent;
- import org.eclipse.swt.events.PaintListener;
- import org.eclipse.swt.graphics.GC;
- import org.eclipse.swt.graphics.Image;
- import org.eclipse.swt.graphics.ImageData;
- import org.eclipse.swt.graphics.ImageLoader;
- import org.eclipse.swt.widgets.Control;
- import org.eclipse.swt.widgets.Display;
- import org.eclipse.swt.widgets.Shell;
- /**
- * 该类管理GIF图像的绘制
- *
- * 注意:该类不会dispose任何资源
- *
- * @author Administrator
- *
- */
- public class SWTGifAnimal {
- /**
- * 存储已注册的图像及控件的映射关系
- */
- private static Map<Image, List<Control>> images = Collections
- .synchronizedMap(new HashMap<Image, List<Control>>());
- /**
- * 存储图像及GIF帧的关系
- */
- private static Map<Image, ImageLoader> loaders = Collections
- .synchronizedMap(new HashMap<Image, ImageLoader>());
- /**
- * 存储图像的当前帧序号
- */
- private static Map<ImageLoader, Integer> currentFrame = Collections
- .synchronizedMap(new HashMap<ImageLoader, Integer>());
- /**
- * 存储图像上一帧的绘制时间
- */
- private static Map<ImageLoader, Long> lastDrawingTime = Collections
- .synchronizedMap(new HashMap<ImageLoader, Long>());
- private static Thread drawingThread = null;;
- private static Thread dispatchThread = null;
- /**
- * 读取GIF图像
- *
- * @param ins
- * @return
- */
- public static ImageLoader loadGif(InputStream ins) {
- ImageLoader loader = new ImageLoader();
- loader.load(ins);
- return loader;
- }
- /**
- * 读取GIF图像
- *
- * @param clazz
- * @param path
- * @return
- */
- public static ImageLoader loadGif(Class clazz, String path) {
- ImageLoader loader = new ImageLoader();
- loader.load(clazz.getResourceAsStream(path));
- return loader;
- }
- /**
- * 创建GIF的第一帧图像
- *
- * @param display
- * @param loader
- * @return
- */
- public static Image createFirstFrame(Display display, ImageLoader loader) {
- return new Image(display, loader.data[0]);
- }
- /**
- * 注册GIF图像
- *
- * @param control
- * GIF图像绘制的控件
- * @param image
- * GIF图像绘制的目标图像
- * @param loader
- * GIF图像帧
- */
- synchronized public static void registerGif(Control control, Image image,
- ImageLoader loader) {
- if (control == null
- || control.isDisposed()
- || loader == null
- || loader.data.length < 1
- || (images.containsKey(image) && images.get(image).contains(
- control))) {
- return;
- }
- if (!images.containsKey(image)) {
- images.put(image, new ArrayList<Control>());
- }
- images.get(image).add(control);
- if (!loaders.containsKey(loader)) {
- loaders.put(image, loader);
- }
- currentFrame.put(loader, 0);
- /*
- * 若线程未启动,则启动绘制和分发线程
- */
- if (drawingThread == null || !drawingThread.isAlive()) {
- drawingThread = new GifDrawingThread();
- drawingThread.start();
- }
- if (dispatchThread == null || !dispatchThread.isAlive()) {
- dispatchThread = new GifFrameDispatchThread();
- dispatchThread.start();
- }
- }
- /**
- * 移除指定图像
- *
- * @param image
- */
- synchronized private static void removeImage(Image image) {
- images.remove(image);
- currentFrame.remove(image);
- lastDrawingTime.remove(image);
- loaders.remove(image);
- if (images.isEmpty()) {
- drawingThread.interrupt();
- dispatchThread.interrupt();
- }
- }
- /**
- * GIF图像绘制线程
- *
- * 该线程根据队列中的任务,在图像中绘制GIF的当前帧,同时请求控件进行重绘
- *
- * @author Administrator
- *
- */
- private static class GifDrawingThread extends Thread {
- /**
- * 存储当前要绘制的GIF帧
- */
- private static Queue<Object[]> loadersQueue = new ConcurrentLinkedQueue<Object[]>();
- private boolean interrupted;
- public GifDrawingThread() {
- super("Gif-Drawing-Thread");
- }
- public void run() {
- Object[] objs = null;
- while (!interrupted) {
- if ((objs = loadersQueue.poll()) == null) {
- // 如果当前绘制任务为空,则休眠5s
- try {
- Thread.sleep(5);
- } catch (InterruptedException e) {
- return;
- }
- continue;
- }
- final Image image = (Image) objs[0]; // 绘制帧的目标图像
- final int frameIndex = (Integer) objs[1]; // 帧的序号
- for (int i = 0; i < (images.get(image) == null ? 0 : images
- .get(image).size()); i++) {
- final Control control = images.get(image).get(i);
- if (control.isDisposed()) {
- // 控件被释放,则移除
- images.get(image).remove(i--);
- if (images.get(image).isEmpty()) {
- // 若当前图像没有被请求绘制的控件,则移除该图像
- removeImage(image);
- }
- continue;
- }
- control.getDisplay().asyncExec(new Runnable() {
- public void run() {
- GC gc = new GC(image);
- ImageLoader loader = loaders.get(image);
- ImageData currentFrameData = loader.data[frameIndex];
- // 创建当前帧图像
- Image frameImage = new Image(control.getDisplay(),
- currentFrameData);
- // 在图片中绘制当前帧
- gc.drawImage(frameImage, currentFrameData.x,
- currentFrameData.y);
- gc.dispose();
- frameImage.dispose();
- // 向控件请求重绘
- control.redraw();
- }
- });
- }
- }
- }
- /**
- * 向队列中加入一个绘制任务
- *
- * @param loader
- * 需要绘制的gif
- * @param frameIndex
- * 需要绘制的gif图像的帧序号
- */
- public static void addDrawingTask(Image image, int frameIndex) {
- loadersQueue.add(new Object[] { image, frameIndex });
- }
- public void interrupt() {
- super.interrupt();
- interrupted = true;
- }
- }
- /**
- * Gif图像帧的派发线程
- *
- * 该线程遍历注册的gif图像,将需要绘制的帧放入GifDrawingThread的任务队列中
- *
- * @author Administrator
- *
- */
- private static class GifFrameDispatchThread extends Thread {
- private boolean interrupted;
- public GifFrameDispatchThread() {
- super("Gif-Frame-Dispatch-Thread");
- }
- public void run() {
- while (!interrupted) {
- for (Entry<Image, ImageLoader> entry : loaders.entrySet()) {
- ImageLoader loader = entry.getValue();
- // 该帧延迟时间
- int delayTime = loader.data[currentFrame.get(loader)].delayTime;
- if (lastDrawingTime.get(loader) != null // 如果并非从未被绘制过
- && lastDrawingTime.get(loader) + delayTime * 10 > System
- .currentTimeMillis()) {
- // 如果与上一帧绘制时间的时间差小于延时时间
- continue;
- }
- // 记录当前帧绘制时间
- lastDrawingTime.put(loader, System.currentTimeMillis());
- // 将该帧加入绘制队列
- GifDrawingThread.addDrawingTask(entry.getKey(),
- currentFrame.get(loader));
- // 定位下一帧
- int nextImage = currentFrame.get(loader) == loader.data.length - 1 ? 0
- : currentFrame.get(loader) + 1;
- currentFrame.put(loader, nextImage);
- }
- try {
- Thread.sleep(5);
- } catch (InterruptedException e) {
- return;
- }
- }
- }
- public void interrupt() {
- super.interrupt();
- interrupted = true;
- }
- }
- /**
- * 示例程序
- *
- * @param args
- */
- public static void main(String[] args) {
- Display display = new Display();
- Shell shell = new Shell(display);
- // 加载图片
- ImageLoader loader = SWTGifAnimal.loadGif(SWTGifAnimal.class,
- "/icons/progressbar.gif");
- // 取第一帧
- final Image image = SWTGifAnimal.createFirstFrame(display, loader);
- shell.addPaintListener(new PaintListener() {
- public void paintControl(PaintEvent evt) {
- evt.gc.drawImage(image, 10, 20);
- }
- });
- // 注册gif
- SWTGifAnimal.registerGif(shell, image, loader);
- shell.setSize(300, 160);
- shell.open();
- while (!shell.isDisposed()) {
- if (display.readAndDispatch()) {
- display.sleep();
- }
- }
- }
- }