package czd.lib.view.gif; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.os.Handler; import android.os.Message; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.ImageView; import java.io.InputStream; /** * GifView<br> * 本类可以显示一个gif动画,其使用方法和android的其它view(如imageview)一样。<br> * 如果要显示的gif太大,会出现OOM的问题。缓冲到tmp * * @author liao */ public class GifView extends ImageView implements GifAction { /** * gif解码器 */ private GifDecoder gifDecoder = null; /** * 当前要画的帧的图 */ private Bitmap currentImage = null; private boolean isRun = true; private boolean pause = false; private DrawThread drawThread = null; private Context context = null; private boolean cacheImage = false; private View backView = null; private GifImageType animationType = GifImageType.SYNC_DECODER; /** * 解码过程中,Gif动画显示的方式<br> * 如果图片较大,那么解码过程会比较长,这个解码过程中,gif如何显示 * * @author liao */ public enum GifImageType { /** * 在解码过程中,不显示图片,直到解码全部成功后,再显示 */ WAIT_FINISH(0), /** * 和解码过程同步,解码进行到哪里,图片显示到哪里 */ SYNC_DECODER(1), /** * 在解码过程中,只显示第一帧图片 */ COVER(2); GifImageType(int i) { nativeInt = i; } final int nativeInt; } public GifView(Context context) { super(context); this.context = context; //gifDecoder = new GifDecoder(this); setScaleType(ImageView.ScaleType.FIT_XY); } public GifView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GifView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); this.context = context; // TypedArray a = context.obtainStyledAttributes(attrs,R.array.); //gifDecoder = new GifDecoder(this); setScaleType(ImageView.ScaleType.FIT_XY); } /** * 设置图片,并开始解码 * * @param gif 要设置的图片 */ private void setGifDecoderImage(byte[] gif) { if (gifDecoder == null) { gifDecoder = new GifDecoder(this); } gifDecoder.setGifImage(gif); gifDecoder.start(); } /** * 设置图片,开始解码 * * @param is 要设置的图片 */ private void setGifDecoderImage(InputStream is) { if (gifDecoder == null) { gifDecoder = new GifDecoder(this); } gifDecoder.setGifImage(is); gifDecoder.start(); } /** * 把本Gif动画设置为另外view的背景 * * @param v 要使用gif作为背景的view */ public void setAsBackground(View v) { backView = v; } protected Parcelable onSaveInstanceState() { super.onSaveInstanceState(); if (gifDecoder != null) gifDecoder.free(); return null; } /** * @hide * 设置缓存图片<br> * 如果缓存图片,每一Frame的间隔太快的话,会出现跳帧的现象<br> * 如果设置了缓存图片,则你必须调用destroy来作缓存图片的清理。 */ // public void setCahceImage(){ // if(gifDecoder == null){ // gifDecoder = new GifDecoder(this); // } // cacheImage = true; // gifDecoder.setCacheImage(true, context); // } /** * 以字节数据形式设置gif图片 * * @param gif 图片 */ public void setGifImage(byte[] gif) { setGifDecoderImage(gif); } /** * 以字节流形式设置gif图片 * * @param is 图片 */ public void setGifImage(InputStream is) { setGifDecoderImage(is); } /** * 以资源形式设置gif图片 * * @param resId gif图片的资源ID */ public void setGifImage(int resId) { Resources r = getResources(); InputStream is = r.openRawResource(resId); setGifDecoderImage(is); } public void destroy() { if (gifDecoder != null) gifDecoder.free(); } /** * 只显示第一帧图片<br> * 调用本方法后,gif不会显示动画,只会显示gif的第一帧图 */ public void showCover() { if (gifDecoder == null) return; pause = true; currentImage = gifDecoder.getImage(); invalidate(); } /** * 继续显示动画<br> * 本方法在调用showCover后,会让动画继续显示,如果没有调用showCover方法,则没有任何效果 */ public void showAnimation() { if (pause) { pause = false; } } /** * 设置gif在解码过程中的显示方式<br> * <strong>本方法只能在setGifImage方法之前设置,否则设置无效</strong> * * @param type 显示方式 */ public void setGifImageType(GifImageType type) { if (gifDecoder == null) animationType = type; } /** * @hide */ public void parseOk(boolean parseStatus, int frameIndex) { if (parseStatus) { if (gifDecoder != null) { switch (animationType) { case WAIT_FINISH: if (frameIndex == -1) { if (gifDecoder.getFrameCount() > 1) { //当帧数大于1时,启动动画线程 DrawThread dt = new DrawThread(); dt.start(); } else { reDraw(); } } break; case COVER: if (frameIndex == 1) { currentImage = gifDecoder.getImage(); reDraw(); } else if (frameIndex == -1) { if (gifDecoder.getFrameCount() > 1) { if (drawThread == null) { drawThread = new DrawThread(); drawThread.start(); } } else { reDraw(); } } break; case SYNC_DECODER: if (frameIndex == 1) { currentImage = gifDecoder.getImage(); reDraw(); } else if (frameIndex == -1) { reDraw(); } else { if (drawThread == null) { drawThread = new DrawThread(); drawThread.start(); } } break; } } else { Log.e("gif", "parse error"); } } } private void reDraw() { if (redrawHandler != null) { Message msg = redrawHandler.obtainMessage(); redrawHandler.sendMessage(msg); } } private void drawImage() { setImageBitmap(currentImage); invalidate(); } private Handler redrawHandler = new Handler() { @SuppressWarnings("deprecation") public void handleMessage(Message msg) { try { if (backView != null) { backView.setBackgroundDrawable(new BitmapDrawable(currentImage)); } else { drawImage(); } } catch (Exception ex) { Log.e("GifView", ex.toString()); } } }; /** * 动画线程 * * @author liao */ private class DrawThread extends Thread { public void run() { if (gifDecoder == null) { return; } while (isRun) { if (gifDecoder.getFrameCount() == 1) { //如果单帧,不进行动画 GifFrame f = gifDecoder.next(); currentImage = f.image; gifDecoder.free(); reDraw(); break; } if (pause == false) { GifFrame frame = gifDecoder.next(); if (frame == null) { SystemClock.sleep(50); continue; } if (frame.image != null) currentImage = frame.image; else if (frame.imageName != null) { currentImage = BitmapFactory.decodeFile(frame.imageName); } long sp = frame.delay; if (redrawHandler != null) { reDraw(); SystemClock.sleep(sp); } else { break; } } else { SystemClock.sleep(50); } } } } }