我个人是比较喜欢逛贴吧的,贴吧里总是会有很多搞笑的动态图片,经常看一看就会感觉欢乐很多,可以释放掉不少平时的压力。确实,比起一张单调的图片,动态图片明显更加的有意思。一般动态图片都是GIF格式的,浏览器中可以直接将这种格式的图片播放成动画。

不过很可惜的是,Android的原生控件并不支持播放GIF格式的图片。我们都知道,在Android中如果想要显示一张图片,可以借助ImageView控件来完成,但是如果将一张GIF图片设置到ImageView里,它只会显示这张图片的第一帧,不会产生任何的动画效果。

那么就没有办法在Android里播放GIF图片了吗?当然不是,我们可以通过自定义控件的方式来实现这个功能。ImageView无法播放GIF图片说明它的功能还不够强大,那么今天我们就来编写一个PowerImageView控件,让它既能支持ImageView控件原生的所有功能,同时还可以播放GIF图片。

下面我们就开始吧,首先新建一个项目,起名就叫PowerImageViewTest,这里使用Android 4.0的API。

由于是要自定义控件,我们还可能会用到一些自定义的属性,因此在values目录下新建一个attrs.xml的文件,可以在这个文件中添加任何需要自定义的属性。这里我们目前只需要一个auto_play属性,代码如下所示:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <declare-styleable name="PowerImageView">
  4. <attr name="auto_play" format="boolean"></attr>
  5. </declare-styleable>
  6. </resources>

完成了这个文件之后,下面我们来开始编写最最重要的PowerImageView类,由于这个类要支持ImageView的所有功能,因此需要让PowerImageView继承自ImageView,代码如下所示:

  1. public class PowerImageView extends ImageView implements OnClickListener {
  2. /**
  3. * 播放GIF动画的关键类
  4. */
  5. private Movie mMovie;
  6. /**
  7. * 开始播放按钮图片
  8. */
  9. private Bitmap mStartButton;
  10. /**
  11. * 记录动画开始的时间
  12. */
  13. private long mMovieStart;
  14. /**
  15. * GIF图片的宽度
  16. */
  17. private int mImageWidth;
  18. /**
  19. * GIF图片的高度
  20. */
  21. private int mImageHeight;
  22. /**
  23. * 图片是否正在播放
  24. */
  25. private boolean isPlaying;
  26. /**
  27. * 是否允许自动播放
  28. */
  29. private boolean isAutoPlay;
  30. /**
  31. * PowerImageView构造函数。
  32. *
  33. * @param context
  34. */
  35. public PowerImageView(Context context) {
  36. super(context);
  37. }
  38. /**
  39. * PowerImageView构造函数。
  40. *
  41. * @param context
  42. */
  43. public PowerImageView(Context context, AttributeSet attrs) {
  44. this(context, attrs, 0);
  45. }
  46. /**
  47. * PowerImageView构造函数,在这里完成所有必要的初始化操作。
  48. *
  49. * @param context
  50. */
  51. public PowerImageView(Context context, AttributeSet attrs, int defStyle) {
  52. super(context, attrs, defStyle);
  53. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PowerImageView);
  54. int resourceId = getResourceId(a, context, attrs);
  55. if (resourceId != 0) {
  56. // 当资源id不等于0时,就去获取该资源的流
  57. InputStream is = getResources().openRawResource(resourceId);
  58. // 使用Movie类对流进行解码
  59. mMovie = Movie.decodeStream(is);
  60. if (mMovie != null) {
  61. // 如果返回值不等于null,就说明这是一个GIF图片,下面获取是否自动播放的属性
  62. isAutoPlay = a.getBoolean(R.styleable.PowerImageView_auto_play, false);
  63. Bitmap bitmap = BitmapFactory.decodeStream(is);
  64. mImageWidth = bitmap.getWidth();
  65. mImageHeight = bitmap.getHeight();
  66. bitmap.recycle();
  67. if (!isAutoPlay) {
  68. // 当不允许自动播放的时候,得到开始播放按钮的图片,并注册点击事件
  69. mStartButton = BitmapFactory.decodeResource(getResources(),
  70. R.drawable.start_play);
  71. setOnClickListener(this);
  72. }
  73. }
  74. }
  75. }
  76. @Override
  77. public void onClick(View v) {
  78. if (v.getId() == getId()) {
  79. // 当用户点击图片时,开始播放GIF动画
  80. isPlaying = true;
  81. invalidate();
  82. }
  83. }
  84. @Override
  85. protected void onDraw(Canvas canvas) {
  86. if (mMovie == null) {
  87. // mMovie等于null,说明是张普通的图片,则直接调用父类的onDraw()方法
  88. super.onDraw(canvas);
  89. } else {
  90. // mMovie不等于null,说明是张GIF图片
  91. if (isAutoPlay) {
  92. // 如果允许自动播放,就调用playMovie()方法播放GIF动画
  93. playMovie(canvas);
  94. invalidate();
  95. } else {
  96. // 不允许自动播放时,判断当前图片是否正在播放
  97. if (isPlaying) {
  98. // 正在播放就继续调用playMovie()方法,一直到动画播放结束为止
  99. if (playMovie(canvas)) {
  100. isPlaying = false;
  101. }
  102. invalidate();
  103. } else {
  104. // 还没开始播放就只绘制GIF图片的第一帧,并绘制一个开始按钮
  105. mMovie.setTime(0);
  106. mMovie.draw(canvas, 0, 0);
  107. int offsetW = (mImageWidth - mStartButton.getWidth()) / 2;
  108. int offsetH = (mImageHeight - mStartButton.getHeight()) / 2;
  109. canvas.drawBitmap(mStartButton, offsetW, offsetH, null);
  110. }
  111. }
  112. }
  113. }
  114. @Override
  115. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  116. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  117. if (mMovie != null) {
  118. // 如果是GIF图片则重写设定PowerImageView的大小
  119. setMeasuredDimension(mImageWidth, mImageHeight);
  120. }
  121. }
  122. /**
  123. * 开始播放GIF动画,播放完成返回true,未完成返回false。
  124. *
  125. * @param canvas
  126. * @return 播放完成返回true,未完成返回false。
  127. */
  128. private boolean playMovie(Canvas canvas) {
  129. long now = SystemClock.uptimeMillis();
  130. if (mMovieStart == 0) {
  131. mMovieStart = now;
  132. }
  133. int duration = mMovie.duration();
  134. if (duration == 0) {
  135. duration = 1000;
  136. }
  137. int relTime = (int) ((now - mMovieStart) % duration);
  138. mMovie.setTime(relTime);
  139. mMovie.draw(canvas, 0, 0);
  140. if ((now - mMovieStart) >= duration) {
  141. mMovieStart = 0;
  142. return true;
  143. }
  144. return false;
  145. }
  146. /**
  147. * 通过Java反射,获取到src指定图片资源所对应的id。
  148. *
  149. * @param a
  150. * @param context
  151. * @param attrs
  152. * @return 返回布局文件中指定图片资源所对应的id,没有指定任何图片资源就返回0。
  153. */
  154. private int getResourceId(TypedArray a, Context context, AttributeSet attrs) {
  155. try {
  156. Field field = TypedArray.class.getDeclaredField("mValue");
  157. field.setAccessible(true);
  158. TypedValue typedValueObject = (TypedValue) field.get(a);
  159. return typedValueObject.resourceId;
  160. } catch (Exception e) {
  161. e.printStackTrace();
  162. } finally {
  163. if (a != null) {
  164. a.recycle();
  165. }
  166. }
  167. return 0;
  168. }
  169. }

这个类的代码注释已经非常详细了,我再来简单地解释一下。可以看到,我们重写了ImageView中所有的构建函数,使得PowerImageView的用法可以和ImageView完全相同。在构造函数中,则是对所有必要的数据进行了初始化操作。首先,我们调用了getResourceId()方法去获取图片资源对应的id值,在getResourceId()方法内部是通过Java的反射机制来进行获取的。得到了图片资源的id后,我们将它转换成InputStream,然后传入到Movie.decodeStream()方法中以解码出Movie对象。如果得到的Movie对象等于null,说明这是一张普通的图片资源,就不再进行任何特殊处理,因为父类ImageView都帮我们处理好了。如果得到的Movie对象不等于null,则说明这是一张GIF图片,接着就要去获取是否允许自动播放、图片的宽高等属性的值。如果不允许自动播放,还要给播放按钮注册点击事件,默认是不允许自动播放的。

接下来会进入到onMeasure()方法中。在这个方法中我们进行判断,如果这是一张GIF图片,则需要将PowerImageView的宽高重定义,使得控件的大小刚好可以放得下这张GIF图片。

再往后就会进入到onDraw()方法中。在这个方法里同样先判断当前是一张普通的图片还是GIF图片,如果是普通的图片就直接调用super.onDraw()方法交给ImageView去处理就好了。如果是GIF图片,则先判断该图是否允许自动播放,允许的话就调用playMovie()方法去播放GIF图片就好,不允许的话则会先在PowerImageView中绘制该GIF图片的第一帧,并在图片上绘制一个播放按钮,当用户点击了播放按钮时,再去调用playMovie()方法去播放GIF图片。

下面我们来看看playMovie()方法中是怎样播放GIF图片的吧。可以看到,首先会对动画开始的时间做下记录,然后对动画持续的时间做下记录,接着使用当前的时间减去动画开始的时间,得到的时间就是此时PowerImageView应该显示的那一帧,然后借助Movie对象将这一帧绘制到屏幕上即可。之后每次调用playMovie()方法都会绘制一帧图片,连贯起来也就形成了GIF动画。注意,这个方法是有返回值的,如果当前时间减去动画开始时间大于了动画持续时间,那就说明动画播放完成了,返回true,否则返回false。

完成了PowerImageView的编写,下面我们就来看一看如何使用它吧,其实非常简单,打开或新建activity_main.xml,代码如下所示:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent" >
  4. <com.example.powerimageviewtest.PowerImageView
  5. android:id="@+id/image_view"
  6. android:layout_width="wrap_content"
  7. android:layout_height="wrap_content"
  8. android:layout_centerInParent="true"
  9. android:src="@drawable/anim"
  10. />
  11. </RelativeLayout>

可以看到,PowerImageView的用法和ImageView几乎完全一样,使用android:src属性来指定一张图片即可,这里指定的anim就是一张GIF图片。然后我们让PowerImageView在布局里居中显示。

MainActivity中的代码都是自动生成的,这里就不再贴出来了。在AndroidManifest.xml中还有一点需要注意,有些4.0以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifest.xml中去禁用硬件加速功能,可以通过指定android:hardwareAccelerated属性来完成,代码如下所示:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.powerimageviewtest"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="14"
  8. android:targetSdkVersion="17" />
  9. <application
  10. android:allowBackup="true"
  11. android:icon="@drawable/ic_launcher"
  12. android:label="@string/app_name"
  13. android:theme="@style/AppTheme"
  14. android:hardwareAccelerated="false"
  15. >
  16. <activity
  17. android:name="com.example.powerimageviewtest.MainActivity"
  18. android:label="@string/app_name" >
  19. <intent-filter>
  20. <action android:name="android.intent.action.MAIN" />
  21. <category android:name="android.intent.category.LAUNCHER" />
  22. </intent-filter>
  23. </activity>
  24. </application>
  25. </manifest>

现在可以来运行一下代码了,一打开程序你就会看到GIF图片的第一帧,点击图片之后就可以播放GIF动画了,如下图所示:

然后我们还可以通过修改activity_main.xml中的代码,给它加上允许自动播放的属性,代码如下所示:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:attr="http://schemas.android.com/apk/res/com.example.powerimageviewtest"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. <com.example.powerimageviewtest.PowerImageView
  6. android:id="@+id/image_view"
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:layout_centerInParent="true"
  10. android:src="@drawable/anim"
  11. attr:auto_play="true"
  12. />
  13. </RelativeLayout>
 这里使用了刚才我们自定义的属性,通过attr:auto_play来启用和禁用自动播放功能。现在将auto_play属性指定成true后,PowerImageView上就不会再显示一个播放按钮,而是会循环地自动播放动画。现在重新运行一下程序,效果如下图所示:

怎么样?效果还不错吧。不仅如此,PowerImageView还继承了ImageView原生的所有功能,只要指定的不是GIF图片,PowerImageView表现的结果就和ImageView完全一致,让我们来放一张普通的PNG图片看看吧,修改activity_main.xml中的代码,如下所示:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent" >
  4. <com.example.powerimageviewtest.PowerImageView
  5. android:id="@+id/image_view"
  6. android:layout_width="wrap_content"
  7. android:layout_height="wrap_content"
  8. android:layout_centerInParent="true"
  9. android:src="@drawable/conan"
  10. />
  11. </RelativeLayout>

这里在src属性里面指定了一张名字为conan的图片,这是一张PNG图片,效果如下图所示:

一张图片在布局正中央显示出来了,正是普通ImageView所具备的功能。你还可以在PowerImageView中指定android:scaleType等属性,用法和原生的ImageView完全一样。怎么样,是不是确实算得上是Power版的ImageView了?

源码下载,请点击这里

Android实现播放GIF动画的强大ImageView的更多相关文章

  1. Android PowerImageView实现,可以播放动画的强大ImageView

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11100315 我个人是比较喜欢逛贴吧的,贴吧里总是会有很多搞笑的动态图片,经常看一 ...

  2. Android只播放gif动画

    使用easygifanimator软件把gif动画打散为图片. 第一步:先上图片素材,以下素材放到res/drawable目录下: 转:http://blog.csdn.net/aminfo/arti ...

  3. android 拓展ImageView播放GIF动画

    原生Android控件ImageView并不能支持播放GiF格式的图片.如果将一张GIF的图片放入ImageView中,它只会显示图片的第一帧,不会产生任何动画效果. Android中播放GIF动画实 ...

  4. Android播放gif动画,增加屏幕掉金币效果

    前言:播放gif的版本有很多,我这边使用Android自带的Movie类播放gif动画,也是在别人的基础上进行修改.有同样需求的朋友可以参考我的demo. 1.效果图如下: 2.部分主要代码 Main ...

  5. android 通过帧动画方式播放Gif动画

    注意:经过本人测试,这个方法很耗内存, 图片一多就崩了.慎用 <1>用工具(photoshop或者FireWorks)将GIF动画图片分解成多个GIF静态图片,然后保存在res\drawa ...

  6. Android 播放Gif 动画

    在Android 中是不支持直接使用Gif 图片关联播放帧动画,如下动画在Android 中是无法播放的: Android 提供了另外一种解决的办法,就是使用AnimationDrawable 这一函 ...

  7. Android高级控件(二)——SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现

    Android高级控件(二)--SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现 写这个的原因呢,也是因为项目中用到了gif动画,虽然网上有很多的架包可以实现,不过我们还 ...

  8. Android开发之三种动画

    转载:http://www.cnblogs.com/angeldevil/archive/2011/12/02/2271096.html http://www.lightskystreet.com/2 ...

  9. Java乔晓松-android中的帧动画FrameByFrame

    先看效果后上代码: 动画开始---- 动画切换的界面---- 动画播放完毕后的跳转界面----- 重要的方法: imageView.setBackgroundResource(R.anim.frame ...

随机推荐

  1. MVC3+EF4.1学习系列(三)-----排序 刷选 以及分页

    上篇文章 已经做出了基本的增删改查    但这远远不足以应付实际的项目  今天讲下实际项目中 肯定会有的 排序 刷选  以及分页. 重点想多写点分页的 毕竟这个是任何时候都要有的 而且 我会尽量把这个 ...

  2. webapi中的自定义路由约束

    Custom Route Constraints You can create custom route constraints by implementing the IHttpRouteConst ...

  3. Excel教程(2) - 函数的参数

    函数右边括号中的部分称为参数,假如一个函数可以使用 多个参数,那么参数与参数之间使用半角逗号进行分隔.参数 可以是常量(数字和文本).逻辑值(例如 TRUE 或 FALSE).数 组.错误值(例如#N ...

  4. digitalocean最新优惠码赠送10美元

    digitalocean是我非常喜欢的vps服务商,目前手头还有十来个digitalocean vps服务器.用了三年多digitalocean后,我发现digitalocean一点小技巧.比如,如果 ...

  5. HttpServletRequest对象(一)

    一:HttpServletRequest介绍: 代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中, 二:Request常用的方法 1):获得客户端信 ...

  6. JIRA搭建

    请参考下面的文章 http://www.linuxidc.com/Linux/2014-09/106995.htm 所需下载的文件 链接: http://pan.baidu.com/s/1c0wad3 ...

  7. Android上的远程调试

    来源: http://www.seejs.com/archives/296 目录 远程调试概述 使用 Chrome 的 ADB 扩展进行远程调试 1. 安装 ADB 扩展 2. 启用你的移动设备上的 ...

  8. Openlayer 3 的画线测量长度

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. shell小脚本工具合集

    1.将指定内容写入文件 echo "hello world" > file.txt echo "hello world" >> file.tx ...

  10. WebDriver获取table的内容(通过动态获取Table单元格的TagName对其innerHTML值进行获取)

    import java.util.ArrayList;import java.util.Iterator;import java.util.LinkedHashMap;import java.util ...