Android轻松实现播放Gif图片

来源:互联网 发布:java collection接口 编辑:程序博客网 时间:2024/06/04 08:31
 

       Android的开发框架为我们的开发提供了不少很棒的控件,我们在开发的时候不需要太多的编码就能轻松方便的使用这些控件,不过有些时候这些系统自带的控件并不能够完全满足我们的需求。这时就需要我们发挥自己的想象力来实现我们特定需求的控件。

       今天为大家带来一款可以展示Gif图片的控件,实现播放Gif图片的方法不止这一种,你也可以选择其他的方式来实现(比如自己写一个库利用JNI调用或者直接用WebView来做展示),不过那就比较麻烦了,Android提供了可以展示类似这样的文件的方法,利用这些方法可以降低实现难度而且效果不会变差。

       展示图片的过程实际上就是我们把图片解码到屏幕上的过程。Android为我们提供了一个叫做Movie的类可以帮我们实现解码。这个控件不但可以用作展示Gif图片也可以当做普通的ImageView控件来使用。下面我们通过代码来详细解释如何实现的

       首先看一下Demo的目录结构

                                                    

       目录结构非常简单,主要用到的类就只有GIFView一个。下面贴出其中的代码为大家详细解释

      

package com.example.gifview;import java.io.InputStream;import java.lang.reflect.Field;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Movie;import android.os.SystemClock;import android.util.AttributeSet;import android.util.TypedValue;import android.widget.ImageView;public class GifView extends ImageView {/** * Movie对象用来解析gif图片 */private Movie gifMovie;/** * 开始时间,用来和当前时间比较,得出什么时间播放gif */private long mMovieStart;/** * gif图片的宽 */private int gifImageWidth;/** * gif图片的高 */private int gifImageHeight;/** * 访问attrs */private AttributeSet attrs;/** * 复写GifView的构造方法 *  * @param context */public GifView(Context context) {super(context);}/** * 复写的GifView的构造方法 *  * @param context * @param attrs */public GifView(Context context, AttributeSet attrs) {super(context, attrs);this.attrs = attrs;loadGifImage();}private void loadGifImage(){AttributeSet attrs = getContext().getResources().getXml(R.id.gifView);//生成一个TypedArray对象TypedArray array = getContext().obtainStyledAttributes(attrs,R.styleable.weatherView);//得到资源idint resourceId = getResourceId(array);//检查资源idif(resourceId == -1){System.out.println("没有获取到图片Id,请检查是否在xml文件里设置了src属性");}// 以流的方式得到gif文件InputStream is = getResources().openRawResource(resourceId);// 用Movie的decodeStream方法解码文件gifMovie = Movie.decodeStream(is);if (gifMovie != null) {Bitmap bitmap = BitmapFactory.decodeStream(is);gifImageWidth = bitmap.getWidth();gifImageHeight = bitmap.getHeight();bitmap.recycle();}}/** *  复写onDraw方法用于绘制View */@Overrideprotected void onDraw(Canvas canvas) {if (gifMovie == null) {// 如果Movie为空说明不是一个gif图像,不是则调用父类方法,此时该控件等同于ImageViewsuper.onDraw(canvas);} else {// 如果Movie不为空则说明是一个gif图像则展示gif图像showGifImage(canvas);// 刷新invalidate();}}/** *  复写onMeasure方法,用来设置Gif宽高 */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (gifMovie != null) {// 设置尺寸setMeasuredDimension(gifImageWidth, gifImageHeight);}}// 封装的展示gif的方法private boolean showGifImage(Canvas canvas) {//得到系统时间long now = SystemClock.uptimeMillis();if (mMovieStart == 0) {// 把开始时间设置为当前时间mMovieStart = now;}int duration = gifMovie.duration();if (duration == 0) {// 如果没有持续时间就设置为100duration = 100;}// 设置间隔时间int relTime = (int) ((now - mMovieStart) % duration);gifMovie.setTime(relTime);//在指定的位置进行绘制,这里是左上角gifMovie.draw(canvas, 0, 0);if ((now - mMovieStart) >= duration) {mMovieStart = 0;return true;}return false;}/** * 解析xml文件里的src传来的参数,需要传入一个TypeArray,Context,和参数数组AttributeSet *  * @param array * @param context * @param attrs * @return */private int getResourceId(TypedArray array) {try {// 得到一个TypedArray里的域Field field = TypedArray.class.getDeclaredField("mValue");// 设置可访问性为truefield.setAccessible(true);// 从类中取得域值TypedValue typeValueObject = (TypedValue) field.get(array);return typeValueObject.resourceId;} catch (Exception e) {e.printStackTrace();} finally {array.recycle();}return -1;}}


       我们先声明一个Movie的对象,准备用来解析Gif图片,然后我们需要一个变量来记录开始播放的时间,用两个整形变量来记录需要播放的Gif图片的宽高,最后我们实现播放Gif的基类是ImageView,相当于ImageView+Movie来实现的这个功能。需要顺便一提的是,这里用到一点关于Java反射的内容,为的是拿到配置文件里的Gif图片ID。代码如下:

      

                //生成一个TypedArray对象TypedArray array = getContext().obtainStyledAttributes(attrs,R.styleable.weatherView);//得到资源idint resourceId = getResourceId(array);

        首先用loadImage()来载入Gif图片,载入的时候需要得到图片的宽高。

        自己实现控件除了继承之外实现onMeasure和onDraw方法也是必要的。在onMeasure里可以设置尺寸,同时可以判断Movie对象是否为空,来决定这次展示是作为普通图片还是Gif图片来展示。

         在showGifImage方法里就是实现展示Gif图片的关键部分,这里需要对Movie的duration属性进行设置。用来控制播放周期。

 

 

       之后的过程就比较容易了,像普通的控件一样来使用就可以了。下面是布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" ><com.example.gifview.GifView     android:id="@+id/gifView"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:src="@drawable/weather_gif"/><TextView     android:id="@+id/textView1"    android:layout_below="@id/gifView"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="演示gif控件"/></RelativeLayout>
 

        这里<com.example.gifview.GifView>就是我们需要的Gif控件。

 

        然后是在Activity里设置布局文件

 


package com.example.gifview;import android.os.Bundle;import android.app.Activity;import android.view.Menu;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}


 

顺便需要提一下,在使用这个空间时可能需要设置硬件加速为false


 

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.gifview"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="18" />    <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.gifview.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>


 点击此处获取博客代码

0 0