Android-自定义View实现ImageView播放gif
http://blog.csdn.net/guolin_blog/article/details/11100315
总体思路是这样的
PowerImageView类继承ImageView类
给PowerImageView类添加自定义属性auto_play
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <com.example.customview.CounterView
- android:layout_width="100dp"
- android:layout_height="100dp"
- android:layout_centerInParent="true" />
- </RelativeLayout>
构造函数中,初始化:
得到资源id,通过id获取流,判断是不是gif图片
如果是gif图片,需要得到:auto_play,图片长度,图片宽度这三个属性
- public PowerImageView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PowerImageView);
- int resourceId = getResourceId(a, context, attrs);
- if (resourceId != 0) {
- // 当资源id不等于0时,就去获取该资源的流
- InputStream is = getResources().openRawResource(resourceId);
- // 使用Movie类对流进行解码
- mMovie = Movie.decodeStream(is);
- if (mMovie != null) {
- // 如果返回值不等于null,就说明这是一个GIF图片,下面获取是否自动播放的属性
- isAutoPlay = a.getBoolean(R.styleable.PowerImageView_auto_play, false);
- Bitmap bitmap = BitmapFactory.decodeStream(is);
- mImageWidth = bitmap.getWidth();
- mImageHeight = bitmap.getHeight();
- bitmap.recycle();
- if (!isAutoPlay) {
- // 当不允许自动播放的时候,得到开始播放按钮的图片,并注册点击事件
- mStartButton = BitmapFactory.decodeResource(getResources(),
- R.drawable.start_play);
- setOnClickListener(this);
- }
- }
- }
- }
得到资源id的方法有两个:
1.反射
- /**
- * 通过Java反射,获取到src指定图片资源所对应的id。
- *
- * @param a
- * @param context
- * @param attrs
- * @return 返回布局文件中指定图片资源所对应的id,没有指定任何图片资源就返回0。
- */
- private int getResourceId(TypedArray a, Context context, AttributeSet attrs) {
- 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;
- }
这个方法可以得到的不仅仅是自定义的属性,给ImageView设置的所有属性都可以一起得到。
2.通过attr的方法
- for (int i = 0; i < attrs.getAttributeCount(); i++)
{
if(attrs.getAttributeName(i).equals("src"))
{
System.out.println(attrs.getAttributeResourceValue(i, 0)+"=========");
return attrs.getAttributeResourceValue(i, 0);
}
}
播放gif的方法:
保存起始时间,判断现在时间-起始时间是否大于动画长度,大于则停
下面我们来看看playMovie()方法中是怎样播放GIF图片的吧。可以看到,首先会对动画开始的时间做下记录,然后对动画持续的时间做下记录,接着使用当前的时间减去动画开始的时间,得到的时间就是此时PowerImageView应该显示的那一帧,然后借助Movie对象将这一帧绘制到屏幕上即可。之后每次调用playMovie()方法都会绘制一帧图片,连贯起来也就形成了GIF动画。注意,这个方法是有返回值的,如果当前时间减去动画开始时间大于了动画持续时间,那就说明动画播放完成了,返回true,否则返回false。
- /**
- * 开始播放GIF动画,播放完成返回true,未完成返回false。
- *
- * @param canvas
- * @return 播放完成返回true,未完成返回false。
- */
- private boolean playMovie(Canvas canvas) {
- long now = SystemClock.uptimeMillis();
- if (mMovieStart == 0) {
- mMovieStart = now;
- }
- int duration = mMovie.duration();
- if (duration == 0) {
- duration = 1000;
- }
- int relTime = (int) ((now - mMovieStart) % duration);
- mMovie.setTime(relTime);
- mMovie.draw(canvas, 0, 0);
- if ((now - mMovieStart) >= duration) {
- mMovieStart = 0;
- return true;
- }
- return false;
- }
绘制gif:
onMeasure规定图片大小,gif则用setMeasuredDimension给定大小
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mMovie != null) {
- // 如果是GIF图片则重写设定PowerImageView的大小
- setMeasuredDimension(mImageWidth, mImageHeight);
- }
- }
onDraw绘制图片,非gir则用默认
起始时绘制开始按钮
isplaying时继续调用play_movie,再invalidate()
- @Override
- protected void onDraw(Canvas canvas) {
- if (mMovie == null) {
- // mMovie等于null,说明是张普通的图片,则直接调用父类的onDraw()方法
- super.onDraw(canvas);
- } else {
- // mMovie不等于null,说明是张GIF图片
- if (isAutoPlay) {
- // 如果允许自动播放,就调用playMovie()方法播放GIF动画
- playMovie(canvas);
- invalidate();
- } else {
- // 不允许自动播放时,判断当前图片是否正在播放
- if (isPlaying) {
- // 正在播放就继续调用playMovie()方法,一直到动画播放结束为止
- if (playMovie(canvas)) {
- isPlaying = false;
- }
- invalidate();
- } else {
- // 还没开始播放就只绘制GIF图片的第一帧,并绘制一个开始按钮
- mMovie.setTime(0);
- mMovie.draw(canvas, 0, 0);
- int offsetW = (mImageWidth - mStartButton.getWidth()) / 2;
- int offsetH = (mImageHeight - mStartButton.getHeight()) / 2;
- canvas.drawBitmap(mStartButton, offsetW, offsetH, null);
- }
- }
- }
- }
然后我们还可以通过修改activity_main.xml中的代码,给它加上允许自动播放的属性,代码如下所示:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:attr="http://schemas.android.com/apk/res/com.example.powerimageviewtest"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <com.example.powerimageviewtest.PowerImageView
- android:id="@+id/image_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- android:src="@drawable/anim"
- attr:auto_play="true"
- />
- </RelativeLayout>
注意:
这里不能对gif的大小进行设置,gif多大,显示出来就是多大。这里主要是演示自定义view,并不是专门实现gif功能
Android-自定义View实现ImageView播放gif的更多相关文章
- Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
- [原] Android 自定义View步骤
例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能 ...
- android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索
我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体 ...
- Android自定义View和控件之一-定制属于自己的UI
照例,拿来主义.我的学习是基于下面的三篇blog.前两是基本的流程,第三篇里有比较细致的绘制相关的属性.第4篇介绍了如何减少布局层次来提高效率. 1. 教你搞定Android自定义View 2. 教你 ...
- Android自定义View(LimitScrollerView-仿天猫广告栏上下滚动效果)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/53303872 本文出自:[openXu的博客] 1分析 2定义组合控件布局 3继承最外层控件 ...
- Android自定义View(二、深入解析自定义属性)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/51468648 本文出自:[openXu的博客] 目录: 为什么要自定义属性 怎样自定义属性 ...
- Android 自定义 View 浅析
Android 自定义 View 浅析 概括 说到自定义 View ,就一定得说说 android 系统的UI绘制流程.再说这个流程之前,我们先看一下在每一个 activity 页面中我们的布局 ui ...
- android 自定义view 前的基础知识
本篇文章是自己自学自定义view前的准备,具体参考资料来自 Android LayoutInflater原理分析,带你一步步深入了解View(一) Android视图绘制流程完全解析,带你一步步深入了 ...
- 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象
前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...
- 【朝花夕拾】Android自定义View篇之(四)自定义View的三种实现方式及自定义属性使用介绍
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/10979161.html],谢谢! 尽管Android系统提供了不少控件,但是有很多酷炫效果仍然 ...
随机推荐
- c++多线程基础2(命名空间 this_thread)
整理自:zh.cppreference.com/w/cpp/thread std::this_thread::yield: 定义于头文件 <thread> 函数原型:void yield( ...
- Ping命令简单报错介绍
了解ABC类IP地址:网络.主机.子网.广播. ---------------------------- 学会ping: ping www.baidu.com 网络检测:ping某一主机可以正常启动! ...
- SDUT OJ 顺序表应用3:元素位置互换之移位算法
顺序表应用3:元素位置互换之移位算法 Time Limit: 1000 ms Memory Limit: 570 KiB Submit Statistic Discuss Problem Descri ...
- hdp 2.06 安装备忘
1,官方安装说明文档 http://docs.hortonworks.com/HDPDocuments/HDP2/HDP-2.0.6.0-Win/bk_installing_hdp_for_windo ...
- Loj 6433. 「PKUSC2018」最大前缀和 (状压dp)
题面 Loj 题解 感觉挺难的啊- 状压\(dp\) 首先,有一个性质 对于一个序列的最大前缀和\(\sum_{i=1}^{p} A[i]\) 显然对于每个\(\sum_{i=p+1}^{x}A[i] ...
- 119th LeetCode Weekly Contest Largest Perimeter Triangle
Given an array A of positive lengths, return the largest perimeter of a triangle with non-zero area, ...
- spark SQL编程
1.编程实现将 RDD 转换为 DataFrame源文件内容如下(包含 id,name,age): 1,Ella,362,Bob,293,Jack,29 请将数据复制保存到 Linux 系统中,命名为 ...
- 《The Python Standard Library》——http模块阅读笔记2
http.server是用来构建HTTP服务器(web服务器)的模块,定义了许多相关的类. 创建及运行服务器的代码一般为: def run(server_class=HTTPServer, handl ...
- vue中promise的使用
vue中promise的使用 promise是处理异步的利器,在之前的文章<ES6之promise>中,我详细介绍了promise的使用, 在文章<js动画实现&&回 ...
- Ace教你一步一步做Android新闻客户端(四) 优化Bitmap大法
我计划着把需要用到的知识分解开来写,趁着我们要开发这款客户端的机会把安卓所有移动客户端开发中的技术贯穿其中,也是我自己成长的过程.By Ace in 20160121 我们开发一款新闻客户端程序,它的 ...