Welcome

首页 / 移动开发 / Android / Android实现可播放GIF动画的ImageView

Android的原生控件并不支持播放GIF格式的图片。我们都知道,在Android中如果想要显示一张图片,可以借助ImageView来完成,但是如果将一张GIF图片设置到ImageView里,它只会显示这张图片的第一帧,不会产生任何的动画效果。今天我们来编写一个自定义的增强型ImageView(继承ImageView),可以播放GIF格式的图片,暂且叫做GifImageView吧。
1.自定义属性
<?xml version="1.0" encoding="utf-8"?> <resources><declare-styleable name="GifImageView"> <attr name="auto_play" format="boolean"></attr></declare-styleable> </resources>
 2.自定义View中获取属性值
 private Movie mMovie;//播放动画需要用到的,系统类 private int mImageWidth;//动画的imageview的宽度 private int mImageHeight;//动画imageview的高度 private long mMovieStart = 0;// 播放开始 private boolean isAutoPlay;//是否自动播放 private Bitmap mStartPlay;//开始按钮 private boolean isPlaying=false;//记录是否正在播放 private float mScale;//图片的缩放比 private int mMeasuredGifWidth;//缩放后图片宽 private int mMeasuredGifHeight;//缩放后图片高 ... private void init(Context context, AttributeSet attrs) { TypedArray attributes = context.obtainStyledAttributes(attrs,R.styleable.GifImageView); // 通过反射拿布局中src的资源id,所以gif文件需要放在布局的src中 int resourceId = getResourceId(attributes, context, attrs); if (resourceId != 0) {// 说明是gif动画// 1.将resourcesId变成流// 2.用Move来decode解析流// 3.获得bitmap的长宽InputStream is = getResources().openRawResource(resourceId);mMovie = Movie.decodeStream(is);if (mMovie != null) {Bitmap bitmap = BitmapFactory.decodeStream(is);mImageWidth = bitmap.getWidth();mImageHeight = bitmap.getHeight();// 用完释放bitmap.recycle();// 获得是否允许自动播放,如果不允许自动播放,则初始化播放按钮isAutoPlay = attributes.getBoolean(R.styleable.GifImageView_auto_play, false);if (!isAutoPlay) { mStartPlay = BitmapFactory.decodeResource(getResources(),R.drawable.start_play); setOnClickListener(this);}} } //回收资源 attributes.recycle(); } /** * 通过反射拿布局中src的资源id ** @param attrs * @param context * @param attributes */ private int getResourceId(TypedArray attributes, Context context, AttributeSet attrs) { try {Field filed = TypedArray.class.getDeclaredField("mValue");filed.setAccessible(true);TypedValue typeValue = (TypedValue) filed.get(attributes);return typeValue.resourceId; } catch (Exception e) {e.printStackTrace(); } } return 0;}
3.重写onMesure()
 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mMovie != null) {/* * Calculate horizontal scaling */float scaleW = 1f;int measureModeWidth = MeasureSpec.getMode(widthMeasureSpec);if (measureModeWidth != MeasureSpec.UNSPECIFIED) { int maximumWidth = MeasureSpec.getSize(widthMeasureSpec); scaleW = (float) mImageWidth / (float) maximumWidth;}/* * calculate vertical scaling */float scaleH = 1f;int measureModeHeight = MeasureSpec.getMode(heightMeasureSpec);if (measureModeHeight != MeasureSpec.UNSPECIFIED) { int maximumHeight = MeasureSpec.getSize(heightMeasureSpec); scaleH = (float) mImageHeight / (float) maximumHeight;}/* * calculate overall scale */mScale = 1f / Math.max(scaleH, scaleW);mMeasuredGifWidth = (int) (mImageWidth * mScale);mMeasuredGifHeight = (int) (mImageHeight * mScale);setMeasuredDimension(mMeasuredGifWidth, mMeasuredGifHeight); } }
4.重写onDraw()
 @Override protected void onDraw(Canvas canvas) { if (mMovie == null) {// mMovie等于null,说明是张普通的图片,则直接调用父类的onDraw()方法super.onDraw(canvas); } else {// mMovie不等于null,说明是张GIF图片if (isAutoPlay) {// 如果允许自动播放,就播放playMovie(canvas);invalidate();} else {// 不允许自动播放的话// 1.判断是否正在播放// 2.获得第一帧的图像// 3.然后添加播放按钮if (isPlaying) { // 如果正在播放就playmoive继续播放 if (playMovie(canvas)) { isPlaying = false; } invalidate();} else { // 第一帧 mMovie.setTime(0); canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.scale(mScale, mScale); mMovie.draw(canvas, 0, 0);// 画 canvas.restore(); // 绘制开始按钮 int offsetW = (mMeasuredGifWidth - mStartPlay.getWidth()) / 2; int offsetH = (mMeasuredGifHeight - mStartPlay.getHeight()) / 2; canvas.drawBitmap(mStartPlay, offsetW, offsetH, null);}} } } /** * 播放gif动画 ** @param canvas */ private boolean playMovie(Canvas canvas) { // 1.获取播放的时间 // 2.如果开始start=0,则认为是开始 // 3.记录播放的时间 // 4.设置进度 // 5.画动画 // 6.如果时间大于了播放的时间,则证明结束 long now = SystemClock.uptimeMillis(); if (mMovieStart == 0) {mMovieStart = now; } int duration = mMovie.duration(); if (duration == 0) {duration = 1000; } //记录gif播放了多少时间 int relTime = (int) ((now - mMovieStart) % duration); mMovie.setTime(relTime);// 设置时间 canvas.save(Canvas.MATRIX_SAVE_FLAG); canvas.scale(mScale, mScale); mMovie.draw(canvas, 0, 0);// 画 canvas.restore(); if ((now - mMovieStart) >= duration) {// 结束mMovieStart = 0;return true; } return false; }
5.添加点击事件
 @Override public void onClick(View v) { if(v.getId()==getId()){isPlaying=true;invalidate(); } }
还有一点需要注意,有些4.0以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifest.xml中去禁用硬件加速功能,可以通过指定android:hardwareAccelerated=false来完成。
--------------------------------------------------------------------------------
现在我们来看看运行后效果如何吧,
布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:attr="http://schemas.android.com/apk/res/com.hx.gifimageview" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.hx.gifimageview.GifImageViewandroid:layout_width="150dip"android:layout_height="150dip"android:layout_margin="10dp"android:src="@drawable/shulan"attr:auto_play="false" /> <com.hx.gifimageview.GifImageViewandroid:layout_width="150dip"android:layout_height="150dip"android:layout_margin="10dp"android:src="@drawable/shulan"attr:auto_play="true" /> <com.hx.gifimageview.GifImageViewandroid:layout_width="150dip"android:layout_height="150dip"android:layout_margin="10dp"android:src="@drawable/jingtai"attr:auto_play="true" /></LinearLayout>

 

图一的auto_play属性为false,会显示第一帧和一个播放按钮,点击后Gif图片会自动运行。
图二的auto_play属性为true,会自动循环播放Gif。
图三我们给的资源是静态图片,因为自定义View继承ImageView,所以具备ImageView所有特性,因此也能显示静态图片。

源码下载:http://xiazai.jb51.net/201609/yuanma/GifImageView(jb51.net).rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。