Android实现可播放GIF动画的ImageView

 更新时间:2016年09月05日 10:35:25   作者:huaxun66  
这篇文章主要为大家详细介绍了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.GifImageView
  android:layout_width="150dip"
  android:layout_height="150dip"
  android:layout_margin="10dp"
  android:src="@drawable/shulan"
  attr:auto_play="false" />

 <com.hx.gifimageview.GifImageView
  android:layout_width="150dip"
  android:layout_height="150dip"
  android:layout_margin="10dp"
  android:src="@drawable/shulan"
  attr:auto_play="true" />

 <com.hx.gifimageview.GifImageView
  android: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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 详解Android中IntentService的使用方法

    详解Android中IntentService的使用方法

    这篇文章主要介绍了Android中IntentService的使用方法,需要的朋友可以参考下
    2015-12-12
  • Android实现纸飞机的简单操作

    Android实现纸飞机的简单操作

    这篇文章主要为大家详细介绍了Android实现纸飞机的简单操作,类似于漂流瓶功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • Android LayoutInflater中 Inflate()方法应用

    Android LayoutInflater中 Inflate()方法应用

    本文主要介绍Android 中Inflate 方法的用法, 在开发Android应用过程中,可以在程序中应用 Inflate()方法加载新布局,希望能帮助有需要的朋友
    2016-07-07
  • Android自定义实现顶部粘性下拉刷新效果

    Android自定义实现顶部粘性下拉刷新效果

    这篇文章主要为大家详细介绍了Android自定义实现顶部粘性下拉刷新效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • 浅谈React Native打包apk的坑

    浅谈React Native打包apk的坑

    下面小编就为大家带来一篇浅谈React Native打包apk的坑。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Android View类与SurfaceView类详解

    Android View类与SurfaceView类详解

    本文主要介绍Android View类与SurfaceView类,这里提供了详细的Android View类和SurfaceView类的使用方法,有兴趣的小伙伴可以参考下
    2016-08-08
  • 详解Android数据存储之SQLCipher数据库加密

    详解Android数据存储之SQLCipher数据库加密

    对于已经ROOT的手机来说的没有任何安全性可以,一旦被利用将会导致数据库数据的泄漏,本篇文章主要介绍了Android数据存储之SQLCipher数据库加密,具有一定的参考价值,有需要的可以了解一下。
    2016-12-12
  • Android编程模拟HOME键功能示例

    Android编程模拟HOME键功能示例

    这篇文章主要介绍了Android编程模拟HOME键功能的方法,结合实例形式分析了Android模拟HOME键的原理与相关事件操作技巧,需要的朋友可以参考下
    2016-10-10
  • android 把float转换成Int的实例讲解

    android 把float转换成Int的实例讲解

    今天小编就为大家分享一篇android 把float转换成Int的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • android仿知乎标题栏随ScrollView滚动变色

    android仿知乎标题栏随ScrollView滚动变色

    这篇文章主要为大家详细介绍了android仿知乎标题栏随ScrollView滚动变色,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06

最新评论