开题:加载GIF的场景在安卓开发中还比较常见,网上也有一些三方法的框架会支持对gif的加载,在上篇博客为大家推荐的图片加载库Glide也支持gif的加载Glide工具类的简单封装,今天给大家分享通过自定义View的方式加载GIf,通过此方式,避免了在java代码中对资源图片的操作,降低了代码的关注度,只需要把被加载的Gif资源文件在xml中声明即可。

我采用的实现方式是Android自带的类 android.graphics.Movie 来加载播放Gif动画也就是把Gif资源当成Movie来处理,按来源分别可以从Gif文件的输入流,文件路径,字节数组中得到Movie的实列,然后我们可以通过操作Movie对象来操作Gif文件。

一、实现分析:

1.以Movie的形式引入Gif资源文件

引入Movie资源的方法签名:

1.Movie decodeStream(InputStream is)
2.Movie decodeFile(String pathName)
3.Movie decodeByteArray(byte[] data, int offset,int length)

1.)我使用的是字节流的方式读入声明在xml引入的gif资源:操作如下

final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GifView);mGifId = array.getResourceId(R.styleable.GifView_gif, -1);mPaused = array.getBoolean(R.styleable.GifView_paused, false);array.recycle();if (mGifId != -1) {byte[] bytes = getGiftBytes();mMovie = Movie.decodeByteArray(bytes, 0, bytes.length);}

2.) 以java代码的方式引入

    /*** 设置gif图资源** @param giftResId*/public void setMovieResource(int giftResId) {this.mGifId = giftResId;byte[] bytes = getGiftBytes();mMovie = Movie.decodeByteArray(bytes, 0, bytes.length);requestLayout();}

上述两种方式中把Gif转成byte的方法如下:

 /*** 将gif图片转换成byte[]** @return byte[]*/private byte[] getGiftBytes() {ByteArrayOutputStream baos = new ByteArrayOutputStream();InputStream is = getResources().openRawResource(mGifId);byte[] b = new byte[1024];int len;try {while ((len = is.read(b, 0, 1024)) != -1) {baos.write(b, 0, len);}baos.flush();} catch (IOException e) {e.printStackTrace();} finally {if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}}return baos.toByteArray();}

2.下面就是通过onDraw方法中不断的去通知更新Movie的当前帧,然后重绘界面

@Overrideprotected void onDraw(Canvas canvas) {if (mMovie != null) {if (!mPaused) {updateAnimationTime();drawMovieFrame(canvas);invalidateView();} else {drawMovieFrame(canvas);}}}

附:这里简单提下,movie对象给我们封装了一个int 类型的duration()方法,调用该方法我们可以获得动画的总时长,

/*** 更新当前显示进度*/private void updateAnimationTime() {long now = android.os.SystemClock.uptimeMillis();// 如果第一帧,记录起始时间if (mMovieStart == 0) {mMovieStart = now;}// 取出动画的时长int dur = mMovie.duration();if (dur == 0) {dur = DEFAULT_MOVIE_DURATION;}// 算出需要显示第几帧mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);}

3.总结:通过以上核心实现分析,我们大概明白其实利用Movie加载GIf的流程为:

引入Gif资源===》把gif转成byte===》利用Movie对象提供的方法计算出gif的时长===》通过onDraw不断的把更新之后的当前帧绘制出来。

二、完整实例

1.java代码部分

/*** desc:利用Movie加载gif* author:xiedong* date:2017/11/21*/
public class GifView extends View {/*** gif动态效果总时长,在未设置时长时默认为1秒*/private static final int DEFAULT_MOVIE_DURATION = 1000;/*** gif图片资源ID*/private int mGifId;/*** Movie实例,用来显示gift图片*/private Movie mMovie;/*** 显示gift图片的动态效果的开始时间*/private long mMovieStart;/*** 动态图当前显示第几帧*/private int mCurrentAnimationTime = 0;/*** 图片离屏幕左边的距离*/private float mLeft;/*** 图片离屏幕上边的距离*/private float mTop;/*** 图片的缩放比例*/private float mScale;/*** 图片在屏幕上显示的宽度*/private int mMeasuredMovieWidth;/*** 图片在屏幕上显示的高度*/private int mMeasuredMovieHeight;/*** 是否显示动画,为true表示显示,false表示不显示*/private boolean mVisible = true;/*** 动画效果是否被暂停*/private volatile boolean mPaused = false;public GifView(Context context) {this(context, null);}public GifView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public GifView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setViewAttributes(context, attrs, defStyleAttr);}/*** @param context  上下文* @param attrs    自定义属性* @param defStyle 默认风格*/@SuppressLint("NewApi")private void setViewAttributes(Context context, AttributeSet attrs,int defStyle) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {setLayerType(View.LAYER_TYPE_SOFTWARE, null);}final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GifView);mGifId = array.getResourceId(R.styleable.GifView_gif, -1);mPaused = array.getBoolean(R.styleable.GifView_paused, false);array.recycle();if (mGifId != -1) {byte[] bytes = getGiftBytes();mMovie = Movie.decodeByteArray(bytes, 0, bytes.length);}}/*** 设置gif图资源** @param giftResId*/public void setMovieResource(int giftResId) {this.mGifId = giftResId;byte[] bytes = getGiftBytes();mMovie = Movie.decodeByteArray(bytes, 0, bytes.length);requestLayout();}/*** 手动设置 Movie对象** @param movie Movie*/public void setMovie(Movie movie) {this.mMovie = movie;requestLayout();}/*** 得到Movie对象** @return Movie*/public Movie getMovie() {return mMovie;}/*** 设置要显示第几帧动画** @param time*/public void setMovieTime(int time) {mCurrentAnimationTime = time;invalidate();}/*** 设置暂停** @param paused*/public void setPaused(boolean paused) {this.mPaused = paused;if (!paused) {mMovieStart = android.os.SystemClock.uptimeMillis()- mCurrentAnimationTime;}invalidate();}/*** 判断gif图是否停止了** @return*/public boolean isPaused() {return this.mPaused;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mMovie != null) {int movieWidth = mMovie.width();int movieHeight = mMovie.height();int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);float scaleW = (float) movieWidth / (float) maximumWidth;mScale = 1f / scaleW;mMeasuredMovieWidth = maximumWidth;mMeasuredMovieHeight = (int) (movieHeight * mScale);setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);} else {setMeasuredDimension(getSuggestedMinimumWidth(),getSuggestedMinimumHeight());}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;mTop = (getHeight() - mMeasuredMovieHeight) / 2f;mVisible = getVisibility() == View.VISIBLE;}@Overrideprotected void onDraw(Canvas canvas) {if (mMovie != null) {if (!mPaused) {updateAnimationTime();drawMovieFrame(canvas);invalidateView();} else {drawMovieFrame(canvas);}}}/*** 重绘*/@SuppressLint("NewApi")private void invalidateView() {if (mVisible) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {postInvalidateOnAnimation();} else {invalidate();}}}/*** 更新当前显示进度*/private void updateAnimationTime() {long now = android.os.SystemClock.uptimeMillis();// 如果第一帧,记录起始时间if (mMovieStart == 0) {mMovieStart = now;}// 取出动画的时长int dur = mMovie.duration();if (dur == 0) {dur = DEFAULT_MOVIE_DURATION;}// 算出需要显示第几帧mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);}/*** 绘制图片** @param canvas 画布*/private void drawMovieFrame(Canvas canvas) {// 设置要显示的帧,绘制即可mMovie.setTime(mCurrentAnimationTime);canvas.save(Canvas.MATRIX_SAVE_FLAG);canvas.scale(mScale, mScale);mMovie.draw(canvas, mLeft / mScale, mTop / mScale);canvas.restore();}@SuppressLint("NewApi")@Overridepublic void onScreenStateChanged(int screenState) {super.onScreenStateChanged(screenState);mVisible = screenState == SCREEN_STATE_ON;invalidateView();}@SuppressLint("NewApi")@Overrideprotected void onVisibilityChanged(View changedView, int visibility) {super.onVisibilityChanged(changedView, visibility);mVisible = visibility == View.VISIBLE;invalidateView();}@Overrideprotected void onWindowVisibilityChanged(int visibility) {super.onWindowVisibilityChanged(visibility);mVisible = visibility == View.VISIBLE;invalidateView();}/*** 将gif图片转换成byte[]** @return byte[]*/private byte[] getGiftBytes() {ByteArrayOutputStream baos = new ByteArrayOutputStream();InputStream is = getResources().openRawResource(mGifId);byte[] b = new byte[1024];int len;try {while ((len = is.read(b, 0, 1024)) != -1) {baos.write(b, 0, len);}baos.flush();} catch (IOException e) {e.printStackTrace();} finally {if (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}}return baos.toByteArray();}
}

2.xml中声明使用:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.zhuandian.gif.MainActivity"><com.zhuandian.gifview.GifViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"app:gif="@mipmap/leaf" /></android.support.constraint.ConstraintLayout>

最后附上我为大家抽成library的github引用传送门,方便大家直接以gradle的方式引入到项目中:安卓自定义View实现Gif加载

安卓自定义View实现加载gif图片相关推荐

  1. 安卓自定义原户型加载进度条的实现

    最近开发公司新产品,设计师设计了一个加载进度的圆弧型界面,觉得挺不错的,先发出效果图来让大家看看 先上完成效果: 从这两张图上,我们可以大致总结出几个ui要点: 1:两层进度条,围成一个大的圆弧,并且 ...

  2. Glide 入门到精通之十八——Module 实例:用自定义尺寸优化加载的图片

    http://mrfu.me/2016/02/28/Glide_Module_Example_Optimizing/ 为何要在指定的尺寸下请求图片 在一个最近的项目中我们与一个多媒体服务端工作,它也是 ...

  3. Android 炫酷自定义 View - 剑气加载

    效果图 原理分析 这个效果仔细看,就是有三个类似月牙形状的元素进行循环转动,我们只需要拆解出一个月牙来做效果即可,最后再将三个月牙组合起来就可以达到最终效果了 月牙 先画一个圆: 再画个大一丢丢的: ...

  4. 安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例

    安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例,图片压缩处理逻辑 本文旨在介绍自定义View的实现 ...

  5. vue自定义指令---处理加载图片失败时出现的碎图,onerror事件

    目录 一.自定义指令 1.局部注册和使用 2.全局注册和使用 二.自定义指令处理图片加载失败(碎图) 一.自定义指令 vue中除v-model.v-show等内置指令之外,还允许注册自定义指令,获取D ...

  6. Glide加载圆形图片和自定义圆角图片和对指定的角加载圆角

    先上效果图: 因注释很细,就不一一解释说明了! 1.Glide加载圆形图片: 自定义GlideCircleTransUtils继承BitmapTransformation,重写transform()方 ...

  7. 安卓自定义view全解:初始化,onDraw函数,onMeasure函数,用户手势事件

    全栈工程师开发手册 (作者:栾鹏) 安卓教程全解 安卓自定义view全解. view类包含如下函数.可供重写. onFinishInflate() 回调方法,当应用从XML加载该组件并用它构建界面之后 ...

  8. android 广告效果图,Android_Android实现加载广告图片和倒计时的开屏布局,这是一个android开屏布局的实例 - phpStudy...

    Android实现加载广告图片和倒计时的开屏布局 这是一个android开屏布局的实例,可以用于加载广告图片和倒计时的布局.程序中设置的LayoutParams,划分额外空间比例为6分之5,具体权重比 ...

  9. Android中如何加载大图片和长图片

    我们在做开发的时候总是会不可避免的遇到加载图片的情况,当图片的尺寸小于ImageView的尺寸的时候,我们当然可以很happy的去直接加载展示.但是如果我们要加载的图片远远大于ImageView的大小 ...

最新文章

  1. Cent0S 7上的图形安装
  2. [转]LIB和DLL的区别与使用
  3. python 对话框的创建及调用_单击按钮创建新对话框
  4. PHP-代码审计-文件包含
  5. Spring5参考指南:事件Event
  6. cxf和spring结合出现问题
  7. 查找列表元素的最大最小值(python)
  8. Swift - 26 - 函数的基础写法
  9. ajax下拉框省市级联动
  10. python - 获取时间戳(10位和13位)
  11. oracle的reliable message等待事件
  12. 背水一战:苹果向三星采购5G基带芯片遭拒 或自行研发
  13. 开发一个iOS应用没有那么容易
  14. Android 中流量、电量、弱网环境怎么测?
  15. u8显示云服务器已离线_u8登录不知道这样的主机
  16. arch linux密码,Arch Linux root密码忘记了怎么办
  17. Nexus Indexer 2.0:增量下载
  18. 怎么快速提高网站打开速度-网站打开速度优化教程技巧
  19. Creator Kit: Beginner Code Unity学习记录 (1)
  20. 那些年你用过最好的键盘

热门文章

  1. Java 中利用Calendar类的方法判断当前年是闰年还是平年
  2. QNX操作系统信息传递
  3. Emacs教程(二)
  4. CST粒子工作室学习笔记———旋转操作
  5. IOS开发(22)之生成IPA文件并安装到越狱后的真机上
  6. 数据结构--图--图的数组存储表示,深度优先搜索遍历和广度优先搜索遍历
  7. request.getParameterMap()
  8. 如何设置每月几日提醒
  9. 数字土地博物馆总体规划
  10. Unix传奇 (下篇)