安卓自定义View实现加载gif图片
来源:互联网 发布:java jstack 编辑:程序博客网 时间:2024/06/03 16:26
开题:加载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); }
/** * 设置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的当前帧,然后重绘界面
@Override protected 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; } @Override protected 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()); } } @Override protected 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; } @Override protected 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") @Override public void onScreenStateChanged(int screenState) { super.onScreenStateChanged(screenState); mVisible = screenState == SCREEN_STATE_ON; invalidateView(); } @SuppressLint("NewApi") @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); mVisible = visibility == View.VISIBLE; invalidateView(); } @Override protected 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.GifView android:layout_width="wrap_content" android:layout_height="wrap_content" app:gif="@mipmap/leaf" /></android.support.constraint.ConstraintLayout>
阅读全文
0 0
- 安卓自定义View实现加载gif图片
- 自定义View实现安卓裁剪图片
- 安卓如何加载GIF图片
- 安卓自定义View实现文字环绕图片
- 安卓加载GIF
- 自定义View_加载.gif图片
- 安卓大图加载-自定义view
- 安卓自定义 View 进阶: 图片文字
- 安卓自定义 View 进阶: 图片文字
- 自定义View来显示gif格式图片
- 安卓自定义View实现钟表
- 安卓自定义View实现钟表
- 安卓之自定义view实现索引
- 安卓自定义View实现课程表
- 安卓自定义View实现图片上传进度显示(仿QQ)
- 安卓自定义View实现图片上传进度显示(仿QQ)
- 安卓自定义View实现图片上传进度显示(仿QQ)
- Android--自定义View加载网络图片,实现缩放,圆形剪裁
- android添加java-library,在控制台输出乱码
- leetcode: 97. Interleaving String
- 字符串初始化
- scrapy实现增量式爬取
- find和xargs
- 安卓自定义View实现加载gif图片
- SQL语句拼接常加 where 1=1 的原因
- JEPF 前台一些方法
- 自定义拦截器
- 为当前用户创建cron服务
- Java文件下载选择路径注意事项
- maven debug操作需安装的插件
- Oracle中间件
- cocos2d学习笔记