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动态图片效果

时间: 2024-12-19 06:55:46

Android API Demos 系列之 — 播放GIF动画的类Movie的相关文章

Android Api Demos登顶之路(四十五)Loader--&amp;gt;Cursor

这个demo演示了类载入器的用法.关于类载入器的使用我们在前面的demo中已经介绍过了 在此再小小的复习一下. 类载入器的使用步骤: * 1.获取类载入器的管理者LoaderManager manager = tent.getLoaderManager(); * 2.初始化loader MainActivity public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedIn

Android Api Demos登顶之路(四十)Fragment--&gt;Layout

这个demo演示了fragment的布局特点.本例中在竖屏的状态下,当我们点击列表的条目时 打一个activity来显示这个条目的详细信息. * 而在横屏的状态下由于屏幕足够宽,我们就在界面上同时显示了两个fragment,一个用来 显示标题,另一个用来显示详细信息. layout目录下创建竖屏显示的布局文件:fragment_layout.xml <?xml version="1.0" encoding="utf-8"?> <FrameLayou

Android Api Demos登顶之路(三十)Display Options

这个demo演示了actionbar的各种布局样式. activity_main.xml <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height=&

Android Api Demos登顶之路(十四)Quick Contacts

这个demeo主要演示的是使内容提供者和内容解析者来获取其它应用的数据.Content Provider为不同应用之间共享数据提供了统一的接口,Android系统的每个Content Provider都定义了一个CONTENT_URI,Android中每个Context对象(如Activity)对含有一个ContentResolver,ContentResolver可以根据CONTENT_URI获取对应的Content Provider. 在本例中使用了startManagingCursor(c

Android Api Demos登顶之路(五十二)Search

android 为所有需要提供查询功能的activity提供了两种查询方式:search dialog 和searchview * 这个demo演示了第一种查询方式. * 基本思路:用户提交查询后,Android系统构造一个Intent并把用户的查询内容放在这个Intent中.然后Android启动你定义的用来处理用户查询的Activity(称为Searchable Activity),并把这个Intent发给该Activity. * 使用SearchDialog的步骤 * 1.在res/xml

Android Api Demos登顶之路(四十七)Loader--&gt;Throttle

这个demo演示了如何利用类加载器对自定义的内容提供者共享的数据进行管理 MainActivity public class MainActivity extends Activity { // 定义主机名,用以拼接Uri,Uri表明了内容提供的地址,外部应用通过Uri访问内容提供者,来实现对数据的增删改查 private static final String AUTHORITY = "com.fishtosky.loaderthrottle"; /* * 本例中我们将自定义的一个数

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

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11100315 我个人是比较喜欢逛贴吧的,贴吧里总是会有很多搞笑的动态图片,经常看一看就会感觉欢乐很多,可以释放掉不少平时的压力.确实,比起一张单调的图片,动态图片明显更加的有意思.一般动态图片都是GIF格式的,浏览器中可以直接将这种格式的图片播放成动画. 不过很可惜的是,Android的原生控件并不支持播放GIF格式的图片.我们都知道,在Android中如果想要显示一张图片,可以

Android PowerImageView实现,可以播放动画的强大ImageView[转]

我个人是比较喜欢逛贴吧的,贴吧里总是会有很多搞笑的动态图片,经常看一看就会感觉欢乐很多,可以释放掉不少平时的压力.确实,比起一张单调的图片,动态图片明显更加的有意思.一般动态图片都是GIF格式的,浏览器中可以直接将这种格式的图片播放成动画. 不过很可惜的是,Android的原生控件并不支持播放GIF格式的图片.我们都知道,在Android中如果想要显示一张图片,可以借助ImageView控件来完成,但是如果将一张GIF图片设置到ImageView里,它只会显示这张图片的第一帧,不会产生任何的动画

【黑马Android】(11)音乐播放器/视频播放器/照相机/常见对话框/notification通知/样式和主题/帧动画/传感器/应用程序反编译与安装

音乐播放器api <LinearLayout 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" android:or