Android 播放gif图片
Android的原生控件并不支持播放GIF格式的图片。我们都知道,在Android中如果想要显示一张图片,可以借助ImageView控件来完成,但是如果将一张GIF图片设置到ImageView里,它只会显示这张图片的第一帧,不会产生任何的动画效果。
那么就没有办法在Android里播放GIF图片了吗?当然不是,我们可以通过自定义控件的方式来实现这个功能。ImageView无法播放GIF
图片说明它的功能还不够强大,那么今天我们就来编写一个PowerImageView控件,让它既能支持ImageView控件原生的所有功能,同时还可
以播放GIF图片。
下面我们就开始吧,首先新建一个项目,起名就叫PowerImageViewTest,这里使用Android 4.0的API。
由于是要自定义控件,我们还可能会用到一些自定义的属性,因此在values目录下新建一个attrs.xml的文件,可以在这个文件中添加任何需要自定义的属性。这里我们目前只需要一个auto_play属性,代码如下所示:
- <?xml version="1.0" encoding="utf-8"?>
 - <resources>
 - <declare-styleable name="PowerImageView">
 - <attr name="auto_play" format="boolean"></attr>
 - </declare-styleable>
 - </resources>
 
完成了这个文件之后,下面我们来开始编写最最重要的PowerImageView类,由于这个类要支持ImageView的所有功能,因此需要让PowerImageView继承自ImageView,代码如下所示:
- public class PowerImageView extends ImageView implements OnClickListener {
 - /**
 - * 播放GIF动画的关键类
 - */
 - private Movie mMovie;
 - /**
 - * 开始播放按钮图片
 - */
 - private Bitmap mStartButton;
 - /**
 - * 记录动画开始的时间
 - */
 - private long mMovieStart;
 - /**
 - * GIF图片的宽度
 - */
 - private int mImageWidth;
 - /**
 - * GIF图片的高度
 - */
 - private int mImageHeight;
 - /**
 - * 图片是否正在播放
 - */
 - private boolean isPlaying;
 - /**
 - * 是否允许自动播放
 - */
 - private boolean isAutoPlay;
 - /**
 - * PowerImageView构造函数。
 - *
 - * @param context
 - */
 - public PowerImageView(Context context) {
 - super(context);
 - }
 - /**
 - * PowerImageView构造函数。
 - *
 - * @param context
 - */
 - public PowerImageView(Context context, AttributeSet attrs) {
 - this(context, attrs, 0);
 - }
 - /**
 - * PowerImageView构造函数,在这里完成所有必要的初始化操作。
 - *
 - * @param context
 - */
 - 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);
 - }
 - }
 - }
 - }
 - @Override
 - public void onClick(View v) {
 - if (v.getId() == getId()) {
 - // 当用户点击图片时,开始播放GIF动画
 - isPlaying = true;
 - 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);
 - }
 - }
 - }
 - }
 - @Override
 - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 - super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 - if (mMovie != null) {
 - // 如果是GIF图片则重写设定PowerImageView的大小
 - setMeasuredDimension(mImageWidth, mImageHeight);
 - }
 - }
 - /**
 - * 开始播放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;
 - }
 - /**
 - * 通过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中所有的构建函数,使得 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,代码如下所示:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 - 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"
 - />
 - </RelativeLayout>
 
可以看到,PowerImageView的用法和ImageView几乎完全一样,使用android:src属性来指定一张图片即可,这里指定的anim就是一张GIF图片。然后我们让PowerImageView在布局里居中显示。
MainActivity中的代码都是自动生成的,这里就不再贴出来了。在AndroidManifest.xml中还有一点需要注意,有些4.0 以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifest.xml中去禁用硬件加速功能,可以通 过指定android:hardwareAccelerated属性来完成,代码如下所示:
- <?xml version="1.0" encoding="utf-8"?>
 - <manifest xmlns:android="http://schemas.android.com/apk/res/android"
 - package="com.example.powerimageviewtest"
 - android:versionCode="1"
 - android:versionName="1.0" >
 - <uses-sdk
 - android:minSdkVersion="14"
 - android:targetSdkVersion="17" />
 - <application
 - android:allowBackup="true"
 - android:icon="@drawable/ic_launcher"
 - android:label="@string/app_name"
 - android:theme="@style/AppTheme"
 - android:hardwareAccelerated="false"
 - >
 - <activity
 - android:name="com.example.powerimageviewtest.MainActivity"
 - android:label="@string/app_name" >
 - <intent-filter>
 - <action android:name="android.intent.action.MAIN" />
 - <category android:name="android.intent.category.LAUNCHER" />
 - </intent-filter>
 - </activity>
 - </application>
 - </manifest>
 
然后我们还可以通过修改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>
 - 这里使用了刚才我们自定义的属性,通过attr:auto_play来启用和禁用自动播放功能。现在将auto_play属性指定成true后,PowerImageView上就不会再显示一个播放按钮,而是会循环地自动播放动画。
- 不仅如此,PowerImageView还继承了ImageView原生的所有功能,只要指定的不是GIF图片,PowerImageView表现 的结果就和ImageView完全一致,让我们来放一张普通的PNG图片看看吧,修改activity_main.xml中的代码,如下所示:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 - 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/conan"
 - />
 - </RelativeLayout>
 
这里在src属性里面指定了一张名字为conan的图片,这是一张PNG图片,效果如下图所示:
一张图片在布局正中央显示出来了,正是普通ImageView所具备的功能。你还可以在PowerImageView中指定
android:scaleType等属性,用法和原生的ImageView完全一样。怎么样,是不是确实算得上是Power版的ImageView了? 
 - 不仅如此,PowerImageView还继承了ImageView原生的所有功能,只要指定的不是GIF图片,PowerImageView表现 的结果就和ImageView完全一致,让我们来放一张普通的PNG图片看看吧,修改activity_main.xml中的代码,如下所示:
 
Android 播放gif图片的更多相关文章
- Android实现GIF图片解码与播放
		
Android实现GIF图片解码与播放 如何在Android中播放GIF图片呢?如果直接按以前的方法,分解图片,可能相对比较麻烦. 今天给大伙介绍一种新的方式,构造自己的Android图片解码帮助类, ...
 - Android播放图片动画
		
1.布局文件中添加ImageView <ImageView android:id="@+id/iv_fan" android:layout_width="wrap_ ...
 - Android显示GIF图片
		
今天我们研究一下怎样在Android手机上显示GIF动态图片 首先须要在src文件夹下新建一个自己定义的View.代码例如以下: </pre><pre name="code ...
 - 53、Gif  控件GifView 的使用,播放gif图片
		
GifView 是一个为了解决android中现在没有直接显示gif的view,只能通过mediaplay来显示这个问题的项目,其用法和 ImageView一样,支持gif图片.可监视GIF是否加载成 ...
 - Atitit.android播放smb 网络邻居视频文件解决方案
		
Atitit.android播放smb 网络邻居视频文件解决方案 Android4.4 1.1. Android4视频播放器不能直接地支持smb协议..子好先转换成个http流 1.2. ES文件浏览 ...
 - android获得ImageView图片的等级
		
android获得ImageView图片的等级问题 要实现的功能如下图,点击分享能显示选中与不选中状态,然后发送是根据状态来实现具体分享功能. 在gridview中有5个子项,每个子元素都有两张图片A ...
 - 解决android:background背景图片被拉伸问题
		
ImageView中XML属性src和background的区别: background会根据ImageView组件给定的长宽进行拉伸,而src就存放的是原图的大小,不会进行拉伸.src是图片内容(前 ...
 - Android  PNG透明图片转JPG格式背景变黑
		
Android PNG透明图片转JPG格式背景变黑 在上传图片是,需要把PNG格式转换成JPG格式的,但是在遇上透明背景时,转过来就变成黑色底图了! 原因是PNG支持透明图而 JPG格式不支持透明底 ...
 - Android 播放视频文件
		
package com.example.myvideo2; import java.io.File; import android.app.Activity; import android.net.U ...
 
随机推荐
- LESSCSS
			
LESSCSS应需求而生 CSS 的语法相对简单,对使用者的要求较低,但同时也带来一些问题:CSS 需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤其对于非前端开发工程师来讲,往往会因 ...
 - 16-js-缓冲运动
			
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
 - destoon实现底部添加你是第几位访问者的方法
			
经常会看到一些网站有类似“您是第位访客”字样的计数统计,这里我们来实现把这个统计功能添加到destoon的底部,显示“你是第几问访问者”的效果.此处的计数器与网站流量统计有区别,记录的是刷新次数,并不 ...
 - 快速入门:触摸输入(使用 C#/VB/C++ 和 XAML 的 Windows 应用商店应用)
			
原文 http://technet.microsoft.com/zh-cn/subscriptions/hh465387 快速入门:触摸输入(使用 C#/VB/C++ 和 XAML 的 Windows ...
 - deflate与gzip
			
gzip是一种数据格式,默认且目前仅使用deflate算法压缩data部分: deflate是一种压缩算法,是huffman编码的一种加强. deflate与gzip解压的代码几乎相同,可以合成一块代 ...
 - bwlabel函数的c++实现
			
实验中需要用到区域联通的算法,就是类似于matlab中bwlabel的函数.网上找了找c++源码未果,bwlabel-python版用python描述了matlab中的实现方法,但是最后对标签的处理部 ...
 - 《Algorithms 4th Edition》读书笔记——2.4 优先队列(priority queue)-Ⅰ
			
许多应用程序都需要处理有序的元素,但不一定要求他们全部有序,或者是不一定要以此就将他们排序.很多情况下我们会手机一些元素,处理当前键值最大的元素,然后再收集更多的元素,再处理当前键值最大的元素.如此这 ...
 - Android核心基础(十)
			
1.音频采集 你可以使用手机进行现场录音,实现步骤如下: 第一步:在功能清单文件AndroidManifest.xml中添加音频刻录权限: <uses-permission android:na ...
 - android中创建模拟器的 SDCard
			
在eclipse中安装了android环境后,可以直接创建AVD和sdcard的,windows->Android Virtual Device Manager,创建一个AVD时,可以同时创建s ...
 - hdu 5642 King's Order(数位dp)
			
Problem Description After the king's speech , everyone is encouraged. But the war is not over. The k ...