Android API Demos 系列之 — 播放GIF动画的类Movie

良好的开端,是成功的一半。A good beginning is half done.  -_-

本文目录

  ● 一、什么是GIF

   二、Android系统和应用如何播放GIF

 

一、【什么是GIF】

  Gif动态图片,是通过对多图层的图片,按时间进行不同的切换,以达到动画的效果。Gif动态图片的特点是,图片小,易于随时安置调用。比起一张单调的静态图片,Gif格式的动态图片明显更加的生动和有意思。

二、【Android系统和应用如何播放GIF】

  在Android系统的原生控件ImageView并不支持播放GIF格式的动态图片。那么Android系统和应用是如何播放Gif格式的动态图片呢?让我们一块去翻看一下Android的API Demos。

  在Android 系统(android-17)提供的API Demos列表—>Graphics—>BitmapDecode界面中,我们可以看到一幅飘杨的旗帜,正是API Demos为我们提供的播放Gif动态图片的示例。对应AndroidADT—>sdk—>samples—>android-17—>ApiDemos中graphics包下的BitmapDecode.java类。

  技术分享

  图1 API Demos Gif动态图示例

  接下来,去翻看一下BitmapDecode.java类的源码。从其中分离出来的相关源码并整理如下:

  1. Copy API Demos源码,自定义实现能够播放Gif动态图片的控件GifMovieView.java

package com.lzz.gifmovie;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Environment;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

/**
 * 自定义控件实现播放Gif动态图片
 * @author lzz
 * Movie.decodeByteArray(data, offset, length);
 * Movie.decodeStream(is);
 * Movie.decodeFile(pathName); //这个方法不能正常显示Gif,需要指定BufferSize
 * You need to use InputStream with a BufferSize while loading .GIF images From SDCARD. 
 * It‘ll work for sure.
 */
public class GifMovieView extends View {
    private static final String TAG = GifMovieView.class.getSimpleName();
    private static final String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;
    /**
     * 这个类目前文档没有API官方说明
     */
    private Movie mMovie; 
    /**
     * 记录起始时间 
     */
    private long mMovieStart;
    /**
     * gif图片文件名(包含后缀.gif)
     */
    private String gifFileName;
    /**
     * gif图片所在项目的资源id
     */
    private int gifResourceId;

    public GifMovieView(Context context) {
        super(context);
        System.out.println("第1个构造方法");
        this.gifResourceId = R.drawable.animated_gif;
        this.gifFileName = "giftest1.gif";
        
        playGifImage(gifResourceId);
        playGifImage(this.gifFileName);
        
    }
    
    public GifMovieView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i(TAG, "第2个构造方法");
        
        this.gifResourceId = R.drawable.animated_gif;
        this.gifFileName = "giftest2.gif";
        
        playGifImage(gifResourceId);
        playGifImage(gifFileName);
    }
    
    public GifMovieView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        Log.i(TAG, "第3个构造方法");
        
        this.gifResourceId = R.drawable.animated_gif;
        this.gifFileName = "giftest3.gif";
        
        playGifImage(gifResourceId);
        playGifImage(gifFileName);
        
    }
    
    
    /**
     * 设置所要播放的gif图片的文件名
     * @param gifFileName
     */
    public void playGifImage(String gifFileName) {
        this.gifFileName = gifFileName;
        File file = new File(externalPath + gifFileName);
        System.out.println("gif文件路径: "+ externalPath + gifFileName);
        if(file.exists()){
            InputStream inputStream = null;
            try {
                inputStream = new BufferedInputStream(new FileInputStream(file));
                byte[] array = streamToBytes(inputStream);
                mMovie = Movie.decodeByteArray(array, 0, array.length);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } finally {
                if(null != inputStream){
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }else{
            Toast.makeText(this.getContext(), "所要播放的gif图片不存在", Toast.LENGTH_LONG).show();
        }
    }
    
    /**
     * 把流转换成byte[]
     * @param is 输入流
     * @return 字节数组
     */
    private static byte[] streamToBytes(InputStream is) {
        ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = is.read(buffer)) >= 0) {
                os.write(buffer, 0, len);
            }
        } catch (java.io.IOException e) {
        }
        return os.toByteArray();
    }
    
    
    /**
     * 设置所要播放的gif图片资源id
     * @param id
     */
    public void playGifImage(int id) {
        if(0 != id){
            this.gifResourceId = id;
            InputStream inputStream = this.getResources().openRawResource(id);
            mMovie =  Movie.decodeStream(inputStream);
        }else{
            Toast.makeText(this.getContext(), "所要播放的gif图片不存在", Toast.LENGTH_LONG).show();
        }
    }

    /**
     * 重载onDraw()方法,实现gif图片的逐帧播放
     */
    @Override
    protected void onDraw(Canvas canvas) {
        long now = SystemClock.uptimeMillis(); // 当前时间
        if(0 == mMovieStart){ // 如果第一帧,记录起始时间
            mMovieStart = now;
        }
        
        if(null != mMovie){
            int mDuration = mMovie.duration(); // 取出动画的时长
            if(0 == mDuration){
                mDuration = 1000;
            }
            // 算出需要显示第几帧
            int realTime = (int)((now - mMovieStart) % mDuration);
            mMovie.setTime(realTime); // 需要显示的帧,绘制即可
            mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight() - mMovie.height());
            invalidate();
            
        }
    }
    
}

 

  2. GifMovieActivity的布局文件activity_gif_movie.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/container"
    android:orientation="vertical" >
    
</LinearLayout>

 

  3. GifMovieActivity.java代码   

package com.lzz.gifmovie;

import android.annotation.SuppressLint;
import android.app.ActionBar.LayoutParams;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.widget.LinearLayout;

/**
 * 测试播放Gif动态图片的自定义控件GifMovieView
 * @author lzz
 * 
 */
@SuppressLint("NewApi") 
public class GifMovieActivity extends ActionBarActivity {
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_gif_movie);
        
        // 获取屏幕的宽高
        DisplayMetrics displayMetrics = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int screenWidth = displayMetrics.widthPixels;
        int screenHeight = displayMetrics.heightPixels;
        System.out.println("手机屏幕分辨率为:" + screenWidth +"*" + screenHeight);
        
        LayoutParams params1 = new LayoutParams(
            LayoutParams.WRAP_CONTENT, screenHeight/3 );
        params1.gravity = Gravity.CENTER;
        
        // 添加一个自定义的GifMovieView控件
        GifMovieView gifMovieView1 = new GifMovieView(this);
        gifMovieView1.setLayoutParams(params1);
        gifMovieView1.playGifImage(R.drawable.animated_gif);
        
        LayoutParams params2 = new LayoutParams(
            LayoutParams.WRAP_CONTENT, screenHeight/2 );
        params2.gravity = Gravity.CENTER;
        
        GifMovieView gifMovieView2 = new GifMovieView(this);
        gifMovieView2.setLayoutParams(params2);
        gifMovieView2.playGifImage("giftest1.gif");
        
        // 往自定义控件上添加前面自定义的两个控件GifMovieView
        LinearLayout linearLayout = (LinearLayout) findViewById(R.id.container);
        linearLayout.addView(gifMovieView1);
        linearLayout.addView(gifMovieView2);
        
    }
    
}

 

 

 

  4. 重点记得在AndroidManifest.xml文中的<application>或者有播放Gif动态图的<activity>结点中

  加入:android:hardwareAccelerated="false" 这一行代码。

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.lzz.gifmovie.GifMovieActivity"
            android:hardwareAccelerated="false"
            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>

  5. 有图有真相:

  技术分享

  图2 自定义控件GifMovieView播放Gif动态图片效果

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。