Android顯示gif

Android爲我們提供的組件裏沒有能顯示Gif圖片的,那麼我們要想實現播放的話 就需要自己封裝一個組件來顯示Gif圖片

需求分析

我們們在寫代碼之前,首先需要分析一下我們所需要的功能:

1.可以自定義GIF圖片是否在加載的時候就播放,如果在加載的時候不播放的話,就點擊一次再播放

2.可以自定義我們的GIF圖片是播放一次就停止,還是一直循環播放

自定義屬性

分析完了我們的需求之後,我們知道,我們至少需要爲我們的自定義控件定義2條屬性

首先在values裏 新建一個 名爲attrs的xml文件,代碼如下

<span style="font-size:14px;"><resources>
    <declare-styleable name="GifImageView">
        <attr name="auto_play" format="boolean" />
        <attr name="is_loop" format="boolean" />
    </declare-styleable>
</resources></span>
這裏的declare-styleable name 就是你這一組自定義屬性的名字,這裏我們通常是和自定義組件的類名一致即可

attr name 就是我們在xml裏使用的時候所能顯示的名字

而 format屬性則代表的是該條屬性的格式它有以下幾種:

reference:參考某一資源ID 
color:顏色值 
boolean:布爾值 
dimension:尺寸值。 
float:浮點值。 
integer:整型值。 
string:字符串 
fraction:百分數。 
enum:枚舉值 
flag:位或運算

需要注意的是,一條屬性可以指定幾種格式,使用 | 連接

GifImageView

接下來我們創建一個自定義組件的類GifImageView

該類繼承自ImageView 因爲我們除了播放動畫的功能外,其他的大部分功能都和ImageView是有重疊的,我們只需要改造它即可,再繼承了之後,我們重寫它的構造方法,重寫前三個參數的 代碼如下:

public class GifImageView extends ImageView {
    public GifImageView(Context context) {
        this(context,null);
    }

    public GifImageView(Context context, AttributeSet attrs) {
        this(context,attrs,0);
    }

    public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

構造方法

這裏需要簡單說明以下這幾個構造方法的用途

一個參數的是在Java代碼裏new出來的時候使用的,兩個參數的是在xml文件裏生命的時候會調用的,而三個參數的則是我們調用自定義屬性的時候會調用的.

在創建完該類了之後,我們就可以先去xml文件裏將我們的組件聲明瞭.代碼如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.lanou.chenfengyao.designdemo.MainActivity">

    <com.lanou.chenfengyao.designdemo.GifImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:auto_play="true"
        app:is_loop="true"
        android:src="@mipmap/testgif"/>
</RelativeLayout>
值得注意的是 我們自定義的屬性前面的命名空間是app,當我們寫app的時候,系統會默認上我們當前app的位置去找我們的自定義屬性,當然,你也可以自己起一個名字,但是在最外層的命名空間上就需要手動指向屬性文件的位置了,一般情況下,寫app比較簡潔

這裏可以看到:我們讓該控件自動播放,並且循環.在xml裏我們的所有代碼就編寫完畢了,接下來回到Java代碼裏 看看是如何播放GIF的.

首先先上代碼:

public class GifImageView extends ImageView {
    private Movie movie;
    private long mMovieStart;
    private int mImageWidth;
    private int mImageHeight;
    private boolean isAutoPlay;
    private boolean isLoop;

    public GifImageView(Context context) {
        this(context,null);
    }

    public GifImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GifImageView);
        int resourceId = getResourceId(attrs);

        if (resourceId != 0) {
            InputStream is = getResources().openRawResource(resourceId);
            movie = Movie.decodeStream(is);

            if (movie != null) {
                isLoop = typedArray.getBoolean(R.styleable.GifImageView_is_loop, true);
                isAutoPlay = typedArray.getBoolean(R.styleable.GifImageView_auto_play, true);
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                mImageWidth = bitmap.getWidth();
                mImageHeight = bitmap.getHeight();
                bitmap.recycle();
            }
        }

        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                isAutoPlay = true;
                invalidate();
            }
        });

    }


    @Override
    protected void onDraw(Canvas canvas) {
        if (movie == null || !isAutoPlay) {
            super.onDraw(canvas);
        } else {
            if (playMovie(canvas)) {
                invalidate();
            }

        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (movie != null) {
            setMeasuredDimension(mImageWidth, mImageHeight);
        }
    }

    private boolean playMovie(Canvas canvas) {
        long now = SystemClock.uptimeMillis();
        if (mMovieStart == 0) {//這是第一次繪製
            mMovieStart = now;
        }
        int duration = movie.duration();

        int relTime = (int) ((now - mMovieStart) % duration);
        movie.setTime(relTime);
        movie.draw(canvas, 0, 0);
        if (((now - mMovieStart) >= duration)) {
            mMovieStart = 0;

            return isLoop;
        }
        return true;
    }

    @Override
    public void setImageResource(@RawRes int resId) {
        if (resId != 0) {
            InputStream is = getResources().openRawResource(resId);
            movie = Movie.decodeStream(is);
        }
        super.setImageResource(resId);
        invalidate();
    }

    //獲取資源ID
    private int getResourceId(AttributeSet attrs){
        for (int i = 0; i < attrs.getAttributeCount(); i++){
            if (attrs.getAttributeName(i).equals("src")){
                return attrs.getAttributeResourceValue(i,0);
            }
        }
        return 0;
    }
}

之後我們來分析一下上面的代碼

我們播放GIF圖片需要用到的核心類是Movie類,該類可以接受一個流,並且根據時間刷新每一幀的畫面,這樣就可以將GIF圖片播放出來了

首先看我們的構造方法

public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.GifImageView);
        int resourceId = getResourceId(attrs);

        if (resourceId != 0) {
            InputStream is = getResources().openRawResource(resourceId);
            movie = Movie.decodeStream(is);

            if (movie != null) {
                isLoop = typedArray.getBoolean(R.styleable.GifImageView_is_loop, true);
                isAutoPlay = typedArray.getBoolean(R.styleable.GifImageView_auto_play, true);
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                mImageWidth = bitmap.getWidth();
                mImageHeight = bitmap.getHeight();
                bitmap.recycle();
            }
        }
<span style="white-space:pre">	</span>typedArray.recycle();
        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                isAutoPlay = true;
                invalidate();
            }
        });

    }

獲得圖片ID

在構造方法裏 我們首先獲得了TypedArray 的對象,這對象裏就存放着我們所有的在xml裏聲明的自定義屬性.接下來我們調用了一個自定義的方法 來獲取在xml文件裏聲明的src屬性,也就是我們的這張圖片.來看一下這個方法

//獲取資源ID
    private int getResourceId(AttributeSet attrs){
        for (int i = 0; i < attrs.getAttributeCount(); i++){
            if (attrs.getAttributeName(i).equals("src")){
                return attrs.getAttributeResourceValue(i,0);
            }
        }
        return 0;
    }
通常在網上找到的方法 都是利用反射 從attrs裏拿到src的屬性值,但是利用反射獲得的Id有個問題,即當在xml裏沒有給src屬性的時候,通過反射一樣會拿到一個數字,所以很難通過返回的數字判斷Id是否爲空.

接着看構造方法,在拿到了資源ID之後 我們就將這張圖片轉換成輸入流,然後再給到movie對象中去,接下來,獲取一下自動播放和是否循環這兩個屬性,默認值我們都給它true; 之後我們將gif圖片轉換成bitmap,主要是爲了拿到它的寬高,去設置給我們的自定義組件,因爲Movie在播放的時候沒法設置寬高,爲了保證一致性,將寬高保持一致.最後設置監聽,當點擊的時候 調用invalidate();這個方法就是重新繪製.

onDraw

之後我們看看onDraw方法,這個方法就是去繪製圖片的

@Override
    protected void onDraw(Canvas canvas) {
        if (movie == null || !isAutoPlay) {
            super.onDraw(canvas);
        } else {
            if (playMovie(canvas)) {
                invalidate();
            }

        }
    }

可以看到,當不播放,或者不是一張GIF的時候,就直接調用父類的繪製方法,否則,會判斷一下,判斷裏的方法是我們自己寫的,來看一下

private boolean playMovie(Canvas canvas) {
        long now = SystemClock.uptimeMillis();
        if (mMovieStart == 0) {//這是第一次繪製
            mMovieStart = now;
        }
        int duration = movie.duration();

        int relTime = (int) ((now - mMovieStart) % duration);
        movie.setTime(relTime);
        movie.draw(canvas, 0, 0);
        if (((now - mMovieStart) >= duration)) {
            mMovieStart = 0;
            return isLoop;
        }
        return true;
    }
該方法就是播放的核心代碼,首先 獲得當前的時間,給到mMovieStart,接下來獲取整個播放Gif動畫的時長,那麼relTime就是當前播放到什麼時間,通過調用movie.setTime方法,就使得movie拿到了這個時間的圖片,然後畫出來就可以了.之後是判斷是否循環的方法,當時間過了持續時間的話,就會去返回isLoop,也就是當只有設置不循環的時候 會返回false,那麼,在onDraw方法裏,當playMovie發揮true的時候,就會持續的去執行invalidate();去刷新當前顯示的圖片,這樣,就實現了播放Gif動畫.

其他方法

最後再補充上 Java代碼裏手動設置Gif圖片的方法就可以了.

總結

到此 播放Gif圖片的自定義組件就基本實現了.

主要參考了

阿瞾的博客

http://blog.csdn.net/XieYupeng520/article/details/46807629#quote

同時改進了它的獲取圖片ID的部分




發佈了29 篇原創文章 · 獲贊 201 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章