【Android实战】记录自学自定义GifView过程,能同时支持gif和其他图片!【实用篇】
2015-07-08 19:41
801 查看
之前写了一篇博客,《【Android实战】记录自学自定义GifView过程,详解属性那些事!【学习篇】》
关于自定义GifView的,详细讲解了学习过程及遇到的一些类的解释,然后完成了一个项目,能通过在xml加入自定义 view (MyGifView)中加入自定义属性(my:gif_src = “@drawable/coffee”),达到播放gif图片的效果。
但是,有几个问题
1.gif_src 属性只支持 gif 图,并不支持其他类型的图片
2.只支持默认的引用图片,不能另外设置
解决思路:
ImageView本身有个属性 src 是定义好的,已经可以用它播放静态图片,如果再能通过它播放动态图片,不就解决问题啦?!
于是查看 ImageView 类的源码,看到构造函数
有没有很眼熟?!对,之前自定义属性的时候用过!这里不过把属性路径改了!之前我们用的是自定义的路径 R.styleable.GifView 。
于是乎,我也想着,要是能在继承类(MyGifView)里面复制上段代码,然后再用movie转化,转换成功说明是 gif,就用之前的方法播放,转换失败说明是其他格式的图片,就交给 ImageView 自己处理!
真是好办法!
然而,根本不能这么用:
不能导入internal的包!
但是思路是对的!
通过参考《 Android PowerImageView实现,可以播放动画的强大ImageView》
得知了可以用反射!
中心代码:
之前在自定义view初始化中的代码,我是用得到自定义属性值的方法获取gif的数据
现在只需要改这一句就好啦!
然后后面都不用改啦!
(但是转换成 InputStream 的时候,还是要加一句判断
好了问题解决!现在可以在xml用src属性指向.gif文件,并且进行正常播放了!
解决思路:
从外面设置无非就是外面调用
那么,重写这些方法,把资源改成我们的 movie 就好啦!so easy!
首先重写
然后在外面(比如MainActivity),调用
but…
出现了一个bug…
就是现在必须在xml里面的自定义MyGifView添加默认的 src 引用 或者 backgroud 附初始值,不然会报错崩溃,如果不想添加默认图片,可以把background设置为透明 #00000000
报错的原因,大概是没设置src属性时,调用反射
按照设置默认src或者backgroud的方法可以暂时解决,如果广大网友知道是什么原因,有什么更好的办法解决它,恳求告知一下!
接着重写
=-=-=-=-=-=-=-=-= 为了完成下面的实现,另花费了很久时间,思绪可能和前面不大连贯了 =-=-=-=-=-=-=-=-=
我本来以为会如
接下来我按顺序讲解下我实现的过程。
首先讲下原来的想法以及为什么后来推翻了。
按着
这个时候我们在Activity加入语句调用setImageDrawable()方法,在运行,出现的是静态图片!
说明代码是有问题的,焦点在drawable转换成inputstream的地方
看下转换类的具体代码 (参考《Android Bitmap与DrawAble与byte[]与InputStream之间的转换工具类》)
注意第三个转换函数
很明显这里将Bitmap压缩为了PNG格式,也就是说GIF被压缩成了PNG!
这个时候很容易想到,把PNG改成GIF不就可以了嘛!~
可是点击进入 CompressFormat 类后发现,只支持三种格式
而这三种都不能实现压缩成gif格式的流。
这个时候我就突发奇想了,能不能不转换成 input stream!看看Movie还支持其他的什么转换方法么!
结果是这样的:
Movie.decodeByteArray(byte[] data, int offset, int length)
Movie.decodeFile(String pathName)
Movie.decodeStream(InputStream is)
1.
可以看到还是要压缩,所以这个方法放弃。
2.
怀着激动的心情运行!
结果 —— 并没有显示任何东西。
—(2015.7.21更新,找到方法破解该问题!可略过下面一大段直接跳到最后看更新内容!)—
希望破灭了~
问度娘要安慰~
检索后,发现大部分情况都是将GIF转换成了PNG/JEPG就不了了之了,去 stackoverflow 发现有人问却也没有人给出行之有效的办法,去Github找各种第三方,发现很多也只是和我【学习篇】实现一样,并不能解决实际问题(我现在项目确实要用到,从sd卡读取图片并显示,支持gif和其他格式图片)。
搜索了一下午,也有了一些思路,大概如下:
方案1.通过先把 gif 图片转换为若干帧的 bitmap 保存起来,然后等要使用的时候,再合成 gif ,并利用线程播放。
(参看:《Android 加载.gif格式图片》。这个方法听上去就比较麻烦,看了代码量又好大我没时 (zi) 间 (xi) 看……)
方案2.自己写一个压缩类,实现压缩 gif 格式图片。
(参看:《java图片压缩处理 支持gif》和《终于搞定多张JPG图片转成GIF动画这个难题,解决方法如下。》。这个方法听上去简单好用,可是代码量好大我没仔 (shi) 细 (jian) 看……)
方案3.通过第三方写好的拿来用,根据需求更改源码。
(参看:《android开源库android-gif-drawable的使用》和“GifView项目源码”。这两个好像看评论第一个更好,用 JNI 解决了内存泄露的问题。但是,我用的是第二个……第二个自带 javadoc,自学没有问题。)
—(2015.8.28更新,还是把我逼到用jni的地步,已经会用,不难,比movie效率高!往后跳过看!)—
懒人看过来:
我是个聪明人,也就是俗称的懒人~我一定是朝着“怎么能快速解决问题”这个方向走的,我现在解决了,但并不是完美地解决办法,鼓励大家都是勤快人,能自己去琢磨~也可以参考我的做法。
首先下载“GifView项目源码”,目录结构是这个样子的
可以看到有个Activity!~那么说明可以直接运行,我们看一下!
哇,感觉好强大的样子。
可是翻阅Activity的实现发现,它都是调用项目res资源的gif,这个我们已经实现了,看看它还有没有其他的设置图片方法?
然后我们看看doc,哇好全的样子(自学就靠它了!)
在GifView的API中,我们发现了三个方法:
经试验,setGifImage(String filePath)可以播放本地(指SD卡)gif,但是也只是支持 .gif 格式的,如果路径目标是其他格式(比如.png),程序就会挂掉。
感觉又回到了原点……忙了一天了,毫无成果,很挫败。
这时候我懒人思想冒了出来:既然是读取文件路径,那就说明可以预先判断后缀名是否是gif,如果是的话就调用该方法,不是调用默认方法不就可以了!
另外,我发现,GifView并没有重写父类的“onDraw()”,”onMeasure()”,“setImageResource(int)”等方法,而我的 MyGifView 刚好写了,于是结合一下,用 MyGifView 继承 GifView!在MyGifView进行修改!
在Activity里面调用
这个时候运行,发现gif和普通图片都能正常显示,但是卡顿非常严重!用GifView的设置方法却不会。
原因我也找到了,是因为我的MyGifView重写了
屏蔽掉之后果然不卡顿了。
最后有人要问了,那我的图片不在本地怎么办呀?我从网上下下来的图片怎么办呀?
我的懒人思想:
方案1. 你下下来先保存嘛……
方案2. 如果你可以得到byte[]数据的话,可以试试Movie的处理byte[]的办法,也可以自己把byte[]转换成InputStream,然后调用setMovie(InputStream),还可以试试用GifView处理byte[]的方法。
总而言之,能绕过bitmap转input stream就好办!
原来这篇博客写了八千字了……该结贴了。
源码地址:https://github.com/Xieyupeng520/MyGifView_V1.1
很幸运的是,我在网上找解决办法的时候,找到了个非常简单的方法,还是用Movie,而且用 Movie 通过流的方式播放 gif 的效果是最好的,和原图无差。这个方法我之前也用过,就是用 Movie 的 decodeFile 方法,以下是 GifView 里面重写父类的 setImageURI 方法。
这个之前试过是不行的。看我找到的解决办法。
将FileInputStream转化为btye[]数组,然后调用Movie.decodeByteArray(byte[] array,0,array.length); 去完成。
写成代码是这个样子:
直接上代码!
不过有个问题是,
参考:《解决Android中Movie导入播放GIF图片文件异常IOException.reset》
我也真是运气,点进去一个“不相关”的问题,都能找到解决办法。所以提醒大家找问题的时候,不要局限于你自己的那几个关键字哦!~
于是后来我用了第三方调用jni的android-gif-drawable开源库。之前不用它就是怕麻烦,用过之后发现不像想象中那样复杂,现在记录一下使用过程。
首先打开《android开源库android-gif-drawable的使用》,过一下前面8点,讲了怎么样把 jni 拷到自己项目中。后面的就是一些 API 了。
简单使用,先新建一个GifDrawable,然后把GifDrawable设置到 GifImageView / GifImageButton / GifTextView 中,就OK了!
值得一提的是,文章当中用的是1.0.8版本,现在虽然已经更新到了1.1.9版本了,但是我用的仍然是1.0.8的,因为1.1.9的so直接导入运行报错,而我还不知道如何解决。
1.0.8版本缺点是不能在Android 5.0 + 的手机上正常运行。
8.28更新-Demo源码下载:https://github.com/Xieyupeng520/MyGifView_V1.3
如果你有任何问题,欢迎留言告诉我!
关于自定义GifView的,详细讲解了学习过程及遇到的一些类的解释,然后完成了一个项目,能通过在xml加入自定义 view (MyGifView)中加入自定义属性(my:gif_src = “@drawable/coffee”),达到播放gif图片的效果。
但是,有几个问题
1.gif_src 属性只支持 gif 图,并不支持其他类型的图片
2.只支持默认的引用图片,不能另外设置
问题一
gif_src 属性只支持 gif 图,并不支持其他类型的图片。解决思路:
ImageView本身有个属性 src 是定义好的,已经可以用它播放静态图片,如果再能通过它播放动态图片,不就解决问题啦?!
于是查看 ImageView 类的源码,看到构造函数
public ImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { //... final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.ImageView, defStyleAttr, defStyleRes); Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src); if (d != null) { setImageDrawable(d); } //... }
有没有很眼熟?!对,之前自定义属性的时候用过!这里不过把属性路径改了!之前我们用的是自定义的路径 R.styleable.GifView 。
于是乎,我也想着,要是能在继承类(MyGifView)里面复制上段代码,然后再用movie转化,转换成功说明是 gif,就用之前的方法播放,转换失败说明是其他格式的图片,就交给 ImageView 自己处理!
真是好办法!
然而,根本不能这么用:
不能导入internal的包!
但是思路是对的!
通过参考《 Android PowerImageView实现,可以播放动画的强大ImageView》
得知了可以用反射!
中心代码:
/** * 通过Java反射,获取到src指定图片资源所对应的id。 * * @param a 属性组 * @param context * @return 返回布局文件中指定图片资源所对应的id,没有指定任何图片资源就返回0。 */ private int getResourceId(TypedArray a, Context context) { try { Field field = TypedArray.class.getDeclaredField("mValue"); field.setAccessible(true); TypedValue typedValueObject = (TypedValue) field.get(a); return typedValueObject.resourceId; } catch (Exception e) { e.printStackTrace(); } finally { if (a != null) { a.recycle(); } } return 0; }
之前在自定义view初始化中的代码,我是用得到自定义属性值的方法获取gif的数据
int resId = typedArray.getResourceId(R.styleable.GifView_gif_src, 0); //gif_src属性对应值
现在只需要改这一句就好啦!
//int resId = typedArray.getResourceId(R.styleable.GifView_gif_src, 0); //gif_src属性对应值 int resId = getResourceId(typedArray, context); //src属性对应值
然后后面都不用改啦!
(但是转换成 InputStream 的时候,还是要加一句判断
if (resId != 0)再进行转换)
if (resId != 0) { InputStream iStream = getResources().openRawResource(resId); //此方法能通过资源文件id查找到资源文件并转化为输入流 mMovie = Movie.decodeStream(iStream); //输入流转化为Movie (mMovie 为全局变量,类型 Movie) }
好了问题解决!现在可以在xml用src属性指向.gif文件,并且进行正常播放了!
问题二
只支持默认的引用图片,不能另外设置解决思路:
从外面设置无非就是外面调用
setImageResource(int resId),
setImageDrawable(Drawable drawable),
setImageBitmap(Bitmap bm)等这些方法去改变 ImageView 属性 src 所对应的值!
那么,重写这些方法,把资源改成我们的 movie 就好啦!so easy!
首先重写
setImageResource(int resId)
@Override public void setImageResource(int resId) { if (resId != 0) { InputStream iStream = getResources().openRawResource(resId); setMovie(iStream); if (mMovie == null) { super.setImageResource(resId); } } else { super.setImageResource(resId); } invalidate(); } /** * 设置movie * @param iStream 输入流 */ public void setMovie(InputStream iStream) { mMovie = Movie.decodeStream(iStream); if (mMovie == null) { //说明不是gif,直接退出 return; } //设置图片宽高 Bitmap bitmap = BitmapFactory.decodeStream(iStream); if (bitmap == null) { return; } mWidth = bitmap.getWidth(); mHeight = bitmap.getHeight(); bitmap.recycle(); }
然后在外面(比如MainActivity),调用
gifView.setImageResource(R.drawable.coffee),可以显示gif,其他格式的图片也可以正常显示。
but…
出现了一个bug…
就是现在必须在xml里面的自定义MyGifView添加默认的 src 引用 或者 backgroud 附初始值,不然会报错崩溃,如果不想添加默认图片,可以把background设置为透明 #00000000
报错的原因,大概是没设置src属性时,调用反射
int resId = getResourceId(typedArray, context);得到的 resId 也并不为0 (具体得到的是什么我也还不知),然后进入 if 语句执行
InputStream iStream = getResources().openRawResource(resId);转换流的时候报了空指针,导致程序崩溃。
按照设置默认src或者backgroud的方法可以暂时解决,如果广大网友知道是什么原因,有什么更好的办法解决它,恳求告知一下!
接着重写
setImageDrawable(Drawable drawable)和
setImageBitmap(Bitmap bm)
=-=-=-=-=-=-=-=-= 为了完成下面的实现,另花费了很久时间,思绪可能和前面不大连贯了 =-=-=-=-=-=-=-=-=
我本来以为会如
setImageResource(int resId)一样顺利,但其实按原来的思路并不可以转换成gif,而是转换成了png/jepg!最后还是借助了三方包才勉强完成任务。
接下来我按顺序讲解下我实现的过程。
首先讲下原来的想法以及为什么后来推翻了。
按着
setImageResource(int resId)的实现思路,
setImageDrawable(Drawable drawable)应该也就是把 drawable 先转换成 input sream,然后再转换成movie,如果成功就说明是gif,不成功说明是其他格式则调用父类方法。
@Override public void setImageDrawable(Drawable drawable) { if(drawable == null) { super.setImageDraable(drawable); } else { mWidth = drawable.getIntrinsicWidth(); //获得宽 mHeight = drawable.getIntrinsicHeight(); //获得高 InputStream iStream = FomatUtils.Drawable2InputStream(drawable); //通过工具类将drawable转换成input stream setMovie(iStream); if (mMovie == null) { //说明不是gif super.setImageDrawable(drawable); } } invalidate(); } @Override public void setImageBitmap(Bitmap bm) { this.setImageDrawable(new BitmapDrawable(getContext().getResources(), bm)); }
这个时候我们在Activity加入语句调用setImageDrawable()方法,在运行,出现的是静态图片!
说明代码是有问题的,焦点在drawable转换成inputstream的地方
InputStream iStream = FomatUtils.Drawable2InputStream(drawable); //通过工具类将drawable转换成input stream
看下转换类的具体代码 (参考《Android Bitmap与DrawAble与byte[]与InputStream之间的转换工具类》)
/** * Bitmap与DrawAble与byte[]与InputStream之间的转换工具类 * @author azz */ public class FormatUtils { /** * drawable -> input stream */ public InputStream Drawable2InputStream(Drawable d) { //drawable -> bitmap Bitmap bitmap = this.Drawable2Bitmap(d); //bitmap -> input stream return this.Bitmap2InputStream(bitmap); } /** * drawable -> bitmap */ public Bitmap Drawable2Bitmap(Drawable drawable) { Bitmap bitmap = Bitmap .createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; } /** * bitmap -> input stream */ public InputStream Bitmap2InputStream(Bitmap bm, int quality) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, quality, baos); //就是这里限制了转换成PNG类型 InputStream is = new ByteArrayInputStream(baos.toByteArray()); return is; } }
注意第三个转换函数
Bitmap2InputStream()方法内的第二句
bm.compress(Bitmap.CompressFormat.PNG, quality, baos);
很明显这里将Bitmap压缩为了PNG格式,也就是说GIF被压缩成了PNG!
这个时候很容易想到,把PNG改成GIF不就可以了嘛!~
可是点击进入 CompressFormat 类后发现,只支持三种格式
public enum CompressFormat { JPEG (0), PNG (1), WEBP (2); //Sdk-14后开始支持 }
而这三种都不能实现压缩成gif格式的流。
这个时候我就突发奇想了,能不能不转换成 input stream!看看Movie还支持其他的什么转换方法么!
结果是这样的:
Movie.decodeByteArray(byte[] data, int offset, int length)
Movie.decodeFile(String pathName)
Movie.decodeStream(InputStream is)
1.
decodeByteArray通过 byte[] 转换的话,无论bitmap还是drawable都要经过下一步骤
/** * bitmap -> byte[] */ public byte[] Bitmap2Bytes(Bitmap bm) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray(); }
可以看到还是要压缩,所以这个方法放弃。
2.
decodeFile通过文件转化的方法好像行之有效!而且Image自带方法
setImageURL(Uri uri),那么重写它看看!
@Override public void setImageURI(Uri uri) { mMovie = Movie.decodeFile(uri.toString()); if (mMovie == null) { super.setImageURI(uri); } }
怀着激动的心情运行!
结果 —— 并没有显示任何东西。
—(2015.7.21更新,找到方法破解该问题!可略过下面一大段直接跳到最后看更新内容!)—
希望破灭了~
问度娘要安慰~
检索后,发现大部分情况都是将GIF转换成了PNG/JEPG就不了了之了,去 stackoverflow 发现有人问却也没有人给出行之有效的办法,去Github找各种第三方,发现很多也只是和我【学习篇】实现一样,并不能解决实际问题(我现在项目确实要用到,从sd卡读取图片并显示,支持gif和其他格式图片)。
搜索了一下午,也有了一些思路,大概如下:
方案1.通过先把 gif 图片转换为若干帧的 bitmap 保存起来,然后等要使用的时候,再合成 gif ,并利用线程播放。
(参看:《Android 加载.gif格式图片》。这个方法听上去就比较麻烦,看了代码量又好大我没时 (zi) 间 (xi) 看……)
方案2.自己写一个压缩类,实现压缩 gif 格式图片。
(参看:《java图片压缩处理 支持gif》和《终于搞定多张JPG图片转成GIF动画这个难题,解决方法如下。》。这个方法听上去简单好用,可是代码量好大我没仔 (shi) 细 (jian) 看……)
方案3.通过第三方写好的拿来用,根据需求更改源码。
(参看:《android开源库android-gif-drawable的使用》和“GifView项目源码”。这两个好像看评论第一个更好,用 JNI 解决了内存泄露的问题。但是,我用的是第二个……第二个自带 javadoc,自学没有问题。)
—(2015.8.28更新,还是把我逼到用jni的地步,已经会用,不难,比movie效率高!往后跳过看!)—
懒人看过来:
我是个聪明人,也就是俗称的懒人~我一定是朝着“怎么能快速解决问题”这个方向走的,我现在解决了,但并不是完美地解决办法,鼓励大家都是勤快人,能自己去琢磨~也可以参考我的做法。
首先下载“GifView项目源码”,目录结构是这个样子的
可以看到有个Activity!~那么说明可以直接运行,我们看一下!
哇,感觉好强大的样子。
可是翻阅Activity的实现发现,它都是调用项目res资源的gif,这个我们已经实现了,看看它还有没有其他的设置图片方法?
然后我们看看doc,哇好全的样子(自学就靠它了!)
在GifView的API中,我们发现了三个方法:
经试验,setGifImage(String filePath)可以播放本地(指SD卡)gif,但是也只是支持 .gif 格式的,如果路径目标是其他格式(比如.png),程序就会挂掉。
感觉又回到了原点……忙了一天了,毫无成果,很挫败。
这时候我懒人思想冒了出来:既然是读取文件路径,那就说明可以预先判断后缀名是否是gif,如果是的话就调用该方法,不是调用默认方法不就可以了!
另外,我发现,GifView并没有重写父类的“onDraw()”,”onMeasure()”,“setImageResource(int)”等方法,而我的 MyGifView 刚好写了,于是结合一下,用 MyGifView 继承 GifView!在MyGifView进行修改!
@Override public void setImageURI(Uri uri) { String path = uri.toString(); if (isGif(path)) { //根据路径名判断后缀是否为gif setGifImage(path); //调用父类GifView的方法 } else { this.pauseGifAnimation(); //暂停之前动画,不然设置别的图片的时候,原来的gif还在播放动画 super.setImageURI(uri); //调用原始父类ImageView的方法 } } //以下两个方法可以写到工具类里 /** * @Description 判断是否是gif图片 * @param path 文件路径 * @return true 是gif; false 不是gif */ public boolean isGif(final String path) { if ("gif".equals(getExtFromFileName)) { return true; } return false; } /** * @Description 获取文件后缀名 * @param fileName 文件名或文件路径 * @return 后缀名 */ public String getExtFromFileName(final String fileName) { int dot = fileName.lastIndexOf('.'); //取得最后一个.的位置 if (dot != -1) { return fileName.substring(dot + 1, fileName.length()); } return ""; }
在Activity里面调用
myGifView.setImageURI(Uri.parse("mnt/sda/sda1/test/coffee.gif")); //myGifView.setImageURI(Uri.parse("mnt/sda/sda1/test/me.png"));
这个时候运行,发现gif和普通图片都能正常显示,但是卡顿非常严重!用GifView的设置方法却不会。
原因我也找到了,是因为我的MyGifView重写了
setImageDrawable(Drawable drawable)和
setImageBitmap(Bitmap bitmap),根据打印发现播放的时候,这两个方法频繁被调用,可能GifView播放动画的时候用到了这两个方法,反正我现在也用不到这两个方法,于是,我就干脆屏蔽掉了。
屏蔽掉之后果然不卡顿了。
最后有人要问了,那我的图片不在本地怎么办呀?我从网上下下来的图片怎么办呀?
我的懒人思想:
方案1. 你下下来先保存嘛……
方案2. 如果你可以得到byte[]数据的话,可以试试Movie的处理byte[]的办法,也可以自己把byte[]转换成InputStream,然后调用setMovie(InputStream),还可以试试用GifView处理byte[]的方法。
总而言之,能绕过bitmap转input stream就好办!
原来这篇博客写了八千字了……该结贴了。
源码地址:https://github.com/Xieyupeng520/MyGifView_V1.1
2015.7.21 更新!解决 Movie.decodeFile 不起作用的问题
本来用上面的第三方解决了也挺好,可是我发现几个问题,第三方的代码播放帧数较多的 gif 图片卡顿非常严重!并且 gif 放大后清晰度也失真严重。很幸运的是,我在网上找解决办法的时候,找到了个非常简单的方法,还是用Movie,而且用 Movie 通过流的方式播放 gif 的效果是最好的,和原图无差。这个方法我之前也用过,就是用 Movie 的 decodeFile 方法,以下是 GifView 里面重写父类的 setImageURI 方法。
@Override public void setImageURI(Uri uri) { mMovie = Movie.decodeFile(uri.toString()); if (mMovie == null) { super.setImageURI(uri); } }
这个之前试过是不行的。看我找到的解决办法。
方法一:
参考了“贴吧五楼”和博客 《android 播放网络或本地gif格式的动态图片》后,得到的解决办法是:将FileInputStream转化为btye[]数组,然后调用Movie.decodeByteArray(byte[] array,0,array.length); 去完成。
写成代码是这个样子:
@Override public void setImageURI(Uri uri) { InputStream is = new FileInputStream(uri.toString()); //把 sream 转换成 byte[] byte[] array = streamToBytes(iStream); mMovie = Movie.decodeByteArray(array, 0, array.length); if (mMovie == null) { super.setImageURI(uri); } //设置图片宽高 Bitmap bitmap = BitmapFactory.decodeByteArray(array, 0, array.length); if (bitmap != null) { mWidth = bitmap.getWidth(); mHeight = bitmap.getHeight(); bitmap.recycle(); //不需要了,释放掉 } } //把 sream 转换成 byte[] private byte[] streamToBytes(InputStream is) { ByteArrayOutputStream os = new ByteArrayOutputStream(1024); //亲测也可不写1024 byte[] buffer = new byte[1024]; //缓存buffer int len; try { while ((len = is.read(buffer)) >= 0) { os.write(buffer, 0, len); //写入输出流 } } catch (Exception e) { e.printStackTrace(); } return os.toByteArray(); }
方法二:
如果你连方法一都嫌麻烦的话,那方法二真的是代码少到死!直接上代码!
@Override public void setImageURI(Uri uri) { //就下面两句是新加的,其他都是原来的 InputStream is = new BufferedInputStream(new FileInputStream(uri.toString())); is.mark(16 * 1024); //调用之前封装好的setMovie(InputStream is)方法 setMovie(is); if (mMovie == null) { super.setImageURI(uri); } } /** * 设置movie * @param iStream 输入流 */ public void setMovie(InputStream iStream) { mMovie = Movie.decodeStream(iStream); if (mMovie == null) { //说明不是gif,直接退出 return; } //设置图片宽高 Bitmap bitmap = BitmapFactory.decodeStream(iStream); if (bitmap == null) { return; } mWidth = bitmap.getWidth(); mHeight = bitmap.getHeight(); bitmap.recycle(); }
不过有个问题是,
Bitmap bitmap = BitmapFactory.decodeStream(iStream);这一句得到的bitmap为null,用方法一不会出现此问题。
参考:《解决Android中Movie导入播放GIF图片文件异常IOException.reset》
我也真是运气,点进去一个“不相关”的问题,都能找到解决办法。所以提醒大家找问题的时候,不要局限于你自己的那几个关键字哦!~
2015.8.28 更新!使用强大的android-gif-drawable开源库,效率比Movie还高!
命运多舛,又出现新问题了,7.21更新的方法在4.0,4.2,5.0的机器上测试都没问题,却发现在Android 4.4出现调用movie.draw(canvas,0,0)崩溃的情况,我也崩溃了。于是后来我用了第三方调用jni的android-gif-drawable开源库。之前不用它就是怕麻烦,用过之后发现不像想象中那样复杂,现在记录一下使用过程。
首先打开《android开源库android-gif-drawable的使用》,过一下前面8点,讲了怎么样把 jni 拷到自己项目中。后面的就是一些 API 了。
简单使用,先新建一个GifDrawable,然后把GifDrawable设置到 GifImageView / GifImageButton / GifTextView 中,就OK了!
GifImageView gif = (GifImageView) findViewById(R.id.hisGifView); GifDrawable gifFromResource = new GifDrawable( getResources(), R.drawable.run ); gif.setImageDrawable(gifFromResource);
值得一提的是,文章当中用的是1.0.8版本,现在虽然已经更新到了1.1.9版本了,但是我用的仍然是1.0.8的,因为1.1.9的so直接导入运行报错,而我还不知道如何解决。
1.0.8版本缺点是不能在Android 5.0 + 的手机上正常运行。
8.28更新-Demo源码下载:https://github.com/Xieyupeng520/MyGifView_V1.3
如果你有任何问题,欢迎留言告诉我!
相关文章推荐
- Android系统结构
- Android Support兼容包 详解
- Android View与GroupView原理以及其子类描述
- Android Design Support Library 的 代码实验——几行代码,让你的 APP 变得花俏
- android 序列化对象 Parcelable
- Android mipmap和drawable
- android学习路线:如何成长为高级工程师
- Android 下载模块分析(DownloadManager和DownloadProvider)
- android名词解释
- Android-PullToRefresh实现进入自动刷新
- 初学_Android4高级编程-8 share preference&文件&状态保存
- Android第四十三期 - 为自己的App支持emjo表情
- Android(java)学习笔记119:继承中父类没有无参构造
- Android中Layout转成Java代码
- Android Studio针对.9图片报Can't have more than one mark
- 开始使用Android Sutdio(五)高效快捷的使用AS
- Android 基本事件
- Android(java)学习笔记118:类继承的注意事项
- Android中的进程和pid
- 【第一篇】学习 android 事件总线androidEventbus之sticky事件的传递