Android显示gif --摘自陈丰尧老师博客160304

原文地址:http://blog.csdn.net/cfy137000/article/details/50536764


Android为我们提供的组件里没有能显示Gif图片的,那么我们要想实现播放的话 就需要自己封装一个组件来显示Gif图片

需求分析

我们们在写代码之前,首先需要分析一下我们所需要的功能:

1.可以自定义GIF图片是否在加载的时候就播放,如果在加载的时候不播放的话,就点击一次再播放

2.可以自定义我们的GIF图片是播放一次就停止,还是一直循环播放

自定义属性

分析完了我们的需求之后,我们知道,我们至少需要为我们的自定义控件定义2条属性

首先在values里 新建一个 名为attrs的xml文件,代码如下

[html] view plain copy

  1. <span style="font-size:14px;"><resources>  

  2.     <declare-styleable name="GifImageView">  

  3.         <attr name="auto_play" format="boolean" />  

  4.         <attr name="is_loop" format="boolean" />  

  5.     </declare-styleable>  

  6. </resources></span>  

这里的declare-styleable name 就是你这一组自定义属性的名字,这里我们通常是和自定义组件的类名一致即可

attr name 就是我们在xml里使用的时候所能显示的名字

而 format属性则代表的是该条属性的格式它有以下几种:

reference:参考某一资源ID 
color:颜色值 
boolean:布尔值 
dimension:尺寸值。 
float:浮点值。 
integer:整型值。 
string:字符串 
fraction:百分数。 
enum:枚举值 
flag:位或运算

需要注意的是,一条属性可以指定几种格式,使用 | 连接

GifImageView

接下来我们创建一个自定义组件的类GifImageView

该类继承自ImageView 因为我们除了播放动画的功能外,其他的大部分功能都和ImageView是有重叠的,我们只需要改造它即可,再继承了之后,我们重写它的构造方法,重写前三个参数的 代码如下:

[java] view plain copy

  1. public class GifImageView extends ImageView {  

  2.     public GifImageView(Context context) {  

  3.         this(context,null);  

  4.     }  

  5.   

  6.     public GifImageView(Context context, AttributeSet attrs) {  

  7.         this(context,attrs,0);  

  8.     }  

  9.   

  10.     public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {  

  11.         super(context, attrs, defStyleAttr);  

  12.     }  

  13. }  

构造方法

这里需要简单说明以下这几个构造方法的用途

一个参数的是在Java代码里new出来的时候使用的,两个参数的是在xml文件里生命的时候会调用的,而三个参数的则是我们调用自定义属性的时候会调用的.

在创建完该类了之后,我们就可以先去xml文件里将我们的组件声明了.代码如下:

[html] view plain copy

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  

  2.     xmlns:tools="http://schemas.android.com/tools"  

  3.     xmlns:app="http://schemas.android.com/apk/res-auto"  

  4.     android:layout_width="match_parent"  

  5.     android:layout_height="match_parent"  

  6.     android:paddingBottom="@dimen/activity_vertical_margin"  

  7.     android:paddingLeft="@dimen/activity_horizontal_margin"  

  8.     android:paddingRight="@dimen/activity_horizontal_margin"  

  9.     android:paddingTop="@dimen/activity_vertical_margin"  

  10.     tools:context="com.lanou.chenfengyao.designdemo.MainActivity">  

  11.   

  12.     <com.lanou.chenfengyao.designdemo.GifImageView  

  13.         android:layout_width="match_parent"  

  14.         android:layout_height="match_parent"  

  15.         app:auto_play="true"  

  16.         app:is_loop="true"  

  17.         android:src="@mipmap/testgif"/>  

  18. </RelativeLayout>  

值得注意的是 我们自定义的属性前面的命名空间是app,当我们写app的时候,系统会默认上我们当前app的位置去找我们的自定义属性,当然,你也可以自己起一个名字,但是在最外层的命名空间上就需要手动指向属性文件的位置了,一般情况下,写app比较简洁

这里可以看到:我们让该控件自动播放,并且循环.在xml里我们的所有代码就编写完毕了,接下来回到Java代码里 看看是如何播放GIF的.

首先先上代码:

[java] view plain copy

  1. public class GifImageView extends ImageView {  

  2.     private Movie movie;  

  3.     private long mMovieStart;  

  4.     private int mImageWidth;  

  5.     private int mImageHeight;  

  6.     private boolean isAutoPlay;  

  7.     private boolean isLoop;  

  8.   

  9.     public GifImageView(Context context) {  

  10.         this(context,null);  

  11.     }  

  12.   

  13.     public GifImageView(Context context, AttributeSet attrs) {  

  14.         this(context, attrs, 0);  

  15.     }  

  16.   

  17.     public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {  

  18.         super(context, attrs, defStyleAttr);  

  19.         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GifImageView);  

  20.         int resourceId = getResourceId(attrs);  

  21.   

  22.         if (resourceId != 0) {  

  23.             InputStream is = getResources().openRawResource(resourceId);  

  24.             movie = Movie.decodeStream(is);  

  25.   

  26.             if (movie != null) {  

  27.                 isLoop = typedArray.getBoolean(R.styleable.GifImageView_is_loop, true);  

  28.                 isAutoPlay = typedArray.getBoolean(R.styleable.GifImageView_auto_play, true);  

  29.                 Bitmap bitmap = BitmapFactory.decodeStream(is);  

  30.                 mImageWidth = bitmap.getWidth();  

  31.                 mImageHeight = bitmap.getHeight();  

  32.                 bitmap.recycle();  

  33.             }  

  34.         }  

  35.   

  36.         this.setOnClickListener(new OnClickListener() {  

  37.             @Override  

  38.             public void onClick(View v) {  

  39.                 isAutoPlay = true;  

  40.                 invalidate();  

  41.             }  

  42.         });  

  43.   

  44.     }  

  45.   

  46.   

  47.     @Override  

  48.     protected void onDraw(Canvas canvas) {  

  49.         if (movie == null || !isAutoPlay) {  

  50.             super.onDraw(canvas);  

  51.         } else {  

  52.             if (playMovie(canvas)) {  

  53.                 invalidate();  

  54.             }  

  55.   

  56.         }  

  57.     }  

  58.   

  59.     @Override  

  60.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  

  61.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  

  62.         if (movie != null) {  

  63.             setMeasuredDimension(mImageWidth, mImageHeight);  

  64.         }  

  65.     }  

  66.   

  67.     private boolean playMovie(Canvas canvas) {  

  68.         long now = SystemClock.uptimeMillis();  

  69.         if (mMovieStart == 0) {//这是第一次绘制  

  70.             mMovieStart = now;  

  71.         }  

  72.         int duration = movie.duration();  

  73.   

  74.         int relTime = (int) ((now - mMovieStart) % duration);  

  75.         movie.setTime(relTime);  

  76.         movie.draw(canvas, 0, 0);  

  77.         if (((now - mMovieStart) >= duration)) {  

  78.             mMovieStart = 0;  

  79.   

  80.             return isLoop;  

  81.         }  

  82.         return true;  

  83.     }  

  84.   

  85.     @Override  

  86.     public void setImageResource(@RawRes int resId) {  

  87.         if (resId != 0) {  

  88.             InputStream is = getResources().openRawResource(resId);  

  89.             movie = Movie.decodeStream(is);  

  90.         }  

  91.         super.setImageResource(resId);  

  92.         invalidate();  

  93.     }  

  94.   

  95.     //获取资源ID  

  96.     private int getResourceId(AttributeSet attrs){  

  97.         for (int i = 0; i < attrs.getAttributeCount(); i++){  

  98.             if (attrs.getAttributeName(i).equals("src")){  

  99.                 return attrs.getAttributeResourceValue(i,0);  

  100.             }  

  101.         }  

  102.         return 0;  

  103.     }  

  104. }  


之后我们来分析一下上面的代码

我们播放GIF图片需要用到的核心类是Movie类,该类可以接受一个流,并且根据时间刷新每一帧的画面,这样就可以将GIF图片播放出来了

首先看我们的构造方法

[java] view plain copy

  1. public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {  

  2.         super(context, attrs, defStyleAttr);  

  3.         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GifImageView);  

  4.         int resourceId = getResourceId(attrs);  

  5.   

  6.         if (resourceId != 0) {  

  7.             InputStream is = getResources().openRawResource(resourceId);  

  8.             movie = Movie.decodeStream(is);  

  9.   

  10.             if (movie != null) {  

  11.                 isLoop = typedArray.getBoolean(R.styleable.GifImageView_is_loop, true);  

  12.                 isAutoPlay = typedArray.getBoolean(R.styleable.GifImageView_auto_play, true);  

  13.                 Bitmap bitmap = BitmapFactory.decodeStream(is);  

  14.                 mImageWidth = bitmap.getWidth();  

  15.                 mImageHeight = bitmap.getHeight();  

  16.                 bitmap.recycle();  

  17.             }  

  18.         }  

  19. <span style="white-space:pre">    </span>typedArray.recycle();  

  20.         this.setOnClickListener(new OnClickListener() {  

  21.             @Override  

  22.             public void onClick(View v) {  

  23.                 isAutoPlay = true;  

  24.                 invalidate();  

  25.             }  

  26.         });  

  27.   

  28.     }  


获得图片ID

在构造方法里 我们首先获得了TypedArray 的对象,这对象里就存放着我们所有的在xml里声明的自定义属性.接下来我们调用了一个自定义的方法 来获取在xml文件里声明的src属性,也就是我们的这张图片.来看一下这个方法

[java] view plain copy

  1. //获取资源ID  

  2.     private int getResourceId(AttributeSet attrs){  

  3.         for (int i = 0; i < attrs.getAttributeCount(); i++){  

  4.             if (attrs.getAttributeName(i).equals("src")){  

  5.                 return attrs.getAttributeResourceValue(i,0);  

  6.             }  

  7.         }  

  8.         return 0;  

  9.     }  

通常在网上找到的方法 都是利用反射 从attrs里拿到src的属性值,但是利用反射获得的Id有个问题,即当在xml里没有给src属性的时候,通过反射一样会拿到一个数字,所以很难通过返回的数字判断Id是否为空.

接着看构造方法,在拿到了资源ID之后 我们就将这张图片转换成输入流,然后再给到movie对象中去,接下来,获取一下自动播放和是否循环这两个属性,默认值我们都给它true; 之后我们将gif图片转换成bitmap,主要是为了拿到它的宽高,去设置给我们的自定义组件,因为Movie在播放的时候没法设置宽高,为了保证一致性,将宽高保持一致.最后设置监听,当点击的时候 调用invalidate();这个方法就是重新绘制.

onDraw

之后我们看看onDraw方法,这个方法就是去绘制图片的

[java] view plain copy

  1. @Override  

  2.     protected void onDraw(Canvas canvas) {  

  3.         if (movie == null || !isAutoPlay) {  

  4.             super.onDraw(canvas);  

  5.         } else {  

  6.             if (playMovie(canvas)) {  

  7.                 invalidate();  

  8.             }  

  9.   

  10.         }  

  11.     }  


可以看到,当不播放,或者不是一张GIF的时候,就直接调用父类的绘制方法,否则,会判断一下,判断里的方法是我们自己写的,来看一下

[java] view plain copy

  1. private boolean playMovie(Canvas canvas) {  

  2.         long now = SystemClock.uptimeMillis();  

  3.         if (mMovieStart == 0) {//这是第一次绘制  

  4.             mMovieStart = now;  

  5.         }  

  6.         int duration = movie.duration();  

  7.   

  8.         int relTime = (int) ((now - mMovieStart) % duration);  

  9.         movie.setTime(relTime);  

  10.         movie.draw(canvas, 0, 0);  

  11.         if (((now - mMovieStart) >= duration)) {  

  12.             mMovieStart = 0;  

  13.             return isLoop;  

  14.         }  

  15.         return true;  

  16.     }  

该方法就是播放的核心代码,首先 获得当前的时间,给到mMovieStart,接下来获取整个播放Gif动画的时长,那么relTime就是当前播放到什么时间,通过调用movie.setTime方法,就使得movie拿到了这个时间的图片,然后画出来就可以了.之后是判断是否循环的方法,当时间过了持续时间的话,就会去返回isLoop,也就是当只有设置不循环的时候 会返回false,那么,在onDraw方法里,当playMovie发挥true的时候,就会持续的去执行invalidate();去刷新当前显示的图片,这样,就实现了播放Gif动画.

其他方法

最后再补充上 Java代码里手动设置Gif图片的方法就可以了.

总结

到此 播放Gif图片的自定义组件就基本实现了.

主要参考了

阿瞾的博客

http://blog.csdn.net/XieYupeng520/article/details/46807629#quote

同时改进了它的获取图片ID的部分


转载于:https://my.oschina.net/u/2531415/blog/647075

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值