Android显示gif
来源:互联网 发布:淘宝详情排版 编辑:程序博客网 时间:2024/06/08 03:38
Android为我们提供的组件里没有能显示Gif图片的,那么我们要想实现播放的话 就需要自己封装一个组件来显示Gif图片
需求分析
我们们在写代码之前,首先需要分析一下我们所需要的功能:
1.可以自定义GIF图片是否在加载的时候就播放,如果在加载的时候不播放的话,就点击一次再播放
2.可以自定义我们的GIF图片是播放一次就停止,还是一直循环播放
自定义属性
分析完了我们的需求之后,我们知道,我们至少需要为我们的自定义控件定义2条属性
首先在values里 新建一个 名为attrs的xml文件,代码如下
<span style="font-size:14px;"><resources> <declare-styleable name="GifImageView"> <attr name="auto_play" format="boolean" /> <attr name="is_loop" format="boolean" /> </declare-styleable></resources></span>这里的declare-styleable name 就是你这一组自定义属性的名字,这里我们通常是和自定义组件的类名一致即可
attr name 就是我们在xml里使用的时候所能显示的名字
而 format属性则代表的是该条属性的格式它有以下几种:
reference:参考某一资源ID
color:颜色值
boolean:布尔值
dimension:尺寸值。
float:浮点值。
integer:整型值。
string:字符串
fraction:百分数。
enum:枚举值
flag:位或运算
需要注意的是,一条属性可以指定几种格式,使用 | 连接
GifImageView
接下来我们创建一个自定义组件的类GifImageView
该类继承自ImageView 因为我们除了播放动画的功能外,其他的大部分功能都和ImageView是有重叠的,我们只需要改造它即可,再继承了之后,我们重写它的构造方法,重写前三个参数的 代码如下:
public class GifImageView extends ImageView { public GifImageView(Context context) { this(context,null); } public GifImageView(Context context, AttributeSet attrs) { this(context,attrs,0); } public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }}
构造方法
这里需要简单说明以下这几个构造方法的用途一个参数的是在Java代码里new出来的时候使用的,两个参数的是在xml文件里生命的时候会调用的,而三个参数的则是我们调用自定义属性的时候会调用的.
在创建完该类了之后,我们就可以先去xml文件里将我们的组件声明了.代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.lanou.chenfengyao.designdemo.MainActivity"> <com.lanou.chenfengyao.designdemo.GifImageView android:layout_width="match_parent" android:layout_height="match_parent" app:auto_play="true" app:is_loop="true" android:src="@mipmap/testgif"/></RelativeLayout>值得注意的是 我们自定义的属性前面的命名空间是app,当我们写app的时候,系统会默认上我们当前app的位置去找我们的自定义属性,当然,你也可以自己起一个名字,但是在最外层的命名空间上就需要手动指向属性文件的位置了,一般情况下,写app比较简洁
这里可以看到:我们让该控件自动播放,并且循环.在xml里我们的所有代码就编写完毕了,接下来回到Java代码里 看看是如何播放GIF的.
首先先上代码:
public class GifImageView extends ImageView { private Movie movie; private long mMovieStart; private int mImageWidth; private int mImageHeight; private boolean isAutoPlay; private boolean isLoop; public GifImageView(Context context) { this(context,null); } public GifImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GifImageView); int resourceId = getResourceId(attrs); if (resourceId != 0) { InputStream is = getResources().openRawResource(resourceId); movie = Movie.decodeStream(is); if (movie != null) { isLoop = typedArray.getBoolean(R.styleable.GifImageView_is_loop, true); isAutoPlay = typedArray.getBoolean(R.styleable.GifImageView_auto_play, true); Bitmap bitmap = BitmapFactory.decodeStream(is); mImageWidth = bitmap.getWidth(); mImageHeight = bitmap.getHeight(); bitmap.recycle(); } } this.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { isAutoPlay = true; invalidate(); } }); } @Override protected void onDraw(Canvas canvas) { if (movie == null || !isAutoPlay) { super.onDraw(canvas); } else { if (playMovie(canvas)) { invalidate(); } } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (movie != null) { setMeasuredDimension(mImageWidth, mImageHeight); } } private boolean playMovie(Canvas canvas) { long now = SystemClock.uptimeMillis(); if (mMovieStart == 0) {//这是第一次绘制 mMovieStart = now; } int duration = movie.duration(); int relTime = (int) ((now - mMovieStart) % duration); movie.setTime(relTime); movie.draw(canvas, 0, 0); if (((now - mMovieStart) >= duration)) { mMovieStart = 0; return isLoop; } return true; } @Override public void setImageResource(@RawRes int resId) { if (resId != 0) { InputStream is = getResources().openRawResource(resId); movie = Movie.decodeStream(is); } super.setImageResource(resId); invalidate(); } //获取资源ID private int getResourceId(AttributeSet attrs){ for (int i = 0; i < attrs.getAttributeCount(); i++){ if (attrs.getAttributeName(i).equals("src")){ return attrs.getAttributeResourceValue(i,0); } } return 0; }}
之后我们来分析一下上面的代码
我们播放GIF图片需要用到的核心类是Movie类,该类可以接受一个流,并且根据时间刷新每一帧的画面,这样就可以将GIF图片播放出来了
首先看我们的构造方法
public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GifImageView); int resourceId = getResourceId(attrs); if (resourceId != 0) { InputStream is = getResources().openRawResource(resourceId); movie = Movie.decodeStream(is); if (movie != null) { isLoop = typedArray.getBoolean(R.styleable.GifImageView_is_loop, true); isAutoPlay = typedArray.getBoolean(R.styleable.GifImageView_auto_play, true); Bitmap bitmap = BitmapFactory.decodeStream(is); mImageWidth = bitmap.getWidth(); mImageHeight = bitmap.getHeight(); bitmap.recycle(); } }<span style="white-space:pre"></span>typedArray.recycle(); this.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { isAutoPlay = true; invalidate(); } }); }
获得图片ID
在构造方法里 我们首先获得了TypedArray 的对象,这对象里就存放着我们所有的在xml里声明的自定义属性.接下来我们调用了一个自定义的方法 来获取在xml文件里声明的src属性,也就是我们的这张图片.来看一下这个方法
//获取资源ID private int getResourceId(AttributeSet attrs){ for (int i = 0; i < attrs.getAttributeCount(); i++){ if (attrs.getAttributeName(i).equals("src")){ return attrs.getAttributeResourceValue(i,0); } } return 0; }通常在网上找到的方法 都是利用反射 从attrs里拿到src的属性值,但是利用反射获得的Id有个问题,即当在xml里没有给src属性的时候,通过反射一样会拿到一个数字,所以很难通过返回的数字判断Id是否为空.
接着看构造方法,在拿到了资源ID之后 我们就将这张图片转换成输入流,然后再给到movie对象中去,接下来,获取一下自动播放和是否循环这两个属性,默认值我们都给它true; 之后我们将gif图片转换成bitmap,主要是为了拿到它的宽高,去设置给我们的自定义组件,因为Movie在播放的时候没法设置宽高,为了保证一致性,将宽高保持一致.最后设置监听,当点击的时候 调用invalidate();这个方法就是重新绘制.
onDraw
之后我们看看onDraw方法,这个方法就是去绘制图片的
@Override protected void onDraw(Canvas canvas) { if (movie == null || !isAutoPlay) { super.onDraw(canvas); } else { if (playMovie(canvas)) { invalidate(); } } }
可以看到,当不播放,或者不是一张GIF的时候,就直接调用父类的绘制方法,否则,会判断一下,判断里的方法是我们自己写的,来看一下
private boolean playMovie(Canvas canvas) { long now = SystemClock.uptimeMillis(); if (mMovieStart == 0) {//这是第一次绘制 mMovieStart = now; } int duration = movie.duration(); int relTime = (int) ((now - mMovieStart) % duration); movie.setTime(relTime); movie.draw(canvas, 0, 0); if (((now - mMovieStart) >= duration)) { mMovieStart = 0; return isLoop; } return true; }该方法就是播放的核心代码,首先 获得当前的时间,给到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的部分
- android 显示gif图片
- Android 显示Gif
- android显示gif图片
- Android显示GIF图片
- Android显示GIF动画
- android activity显示gif
- android显示gif图片
- Android显示GIF动画
- android 显示gif图片
- android显示GIF图片
- Android显示GIF动画
- Android显示gif图片
- Android显示GIF图片
- android 显示gif图片
- Android显示GIF图片
- Android 显示gif动画
- Android显示Gif图片
- Android显示GIF动画
- Android开发之EditText 详解(addTextChangedListener监听用户输入状态)
- 关于验证表单重复提交的问题
- 使用MASM 5编译程序的便利批处理
- DOM事件探秘之三-键盘事件
- a valid provisioning profile for this executable was not found异常
- Android显示gif
- 具名参数
- ITOO框架简单了解
- 【bzoj2216】[Poi2011]Lightning Conductor 决策单调性+整体二分
- http 之session和cookie
- 谨慎对待技术僵尸
- Yii中CDbCriteria常用总结
- 父DIV padding和子DIV margin
- JSON