Android動畫解析(一)—— Frame Animation(幀動畫)

來源:互聯網
上載者:User

Android動畫解析(一)—— Frame Animation(幀動畫)

動畫在我們實際開發中佔有很重要的地位,一個優秀的動畫能為我們的app應用增色很多,同時一個優秀的動畫銜接能夠增加我們app的邏輯展示。在Android系統中,系統給我們提供了幾種動畫的支援,分別是Frame Animation(幀動畫)、Tween Animation(補間動畫)以及3.0系統以後增加的Property Animator(屬性動畫)。這些動畫的熟練使用可以協助我們設計出perfect效果的動畫,下面就開始我們的學習吧!

一、概述
  幀動畫,顧名思義就是這個動畫的效果是由一幀幀的圖片組合出來的。通過制定圖片展示的順序,達到動畫的展示效果。

  在Android開發中,系統給我們提供了”animation-list” 節點用於我們配置幀動畫。

實現步驟

1、在res目錄下建立用於儲存xml動畫檔案的anim檔案夾,res/anim,也可以放在drawable目錄下

2、動畫配置,在animation-list節點中配置item項

2、將檔案設定到ImageView控制項的背景上,然後擷取背景轉換為AnimationDrawable對象進行播放動畫

    iv_imageView.setBackgroundResource(R.drawable.frame_animation);    AnimationDrawable animation = (AnimationDrawable)iv_imageView.getBackground();    animation.start();

二、執行個體講解

1、奔跑的飛馬
  我們先來看下,第一個執行個體,飛奔的飛馬。

  對於這種動畫效果使用Frame Animation動畫即可完成,我們只需要將gif動畫進行分幀切割成圖片,然後我們在animation-list標籤中指定圖片的順序進行播放即可。

代碼實現

<code class=" hljs xml"><animation-list xmlns:android="http://schemas.android.com/apk/res/android">        <item android:drawable="@mipmap/Horse_start" android:duration="200">        <item android:drawable="@mipmap/Horse1" android:duration="200">        <item android:drawable="@mipmap/Horse2" android:duration="200">        <item android:drawable="@mipmap/Horse3" android:duration="200">        <item android:drawable="@mipmap/Horse4" android:duration="200">        <item android:drawable="@mipmap/Horse5" android:duration="200">        <item android:drawable="@mipmap/Horse6" android:duration="200">        <item android:drawable="@mipmap/Horse7" android:duration="200">        <item android:drawable="@mipmap/Horse8" android:duration="200">        <item android:drawable="@mipmap/Horse_start" android:duration="200">    </item></item></item></item></item></item></item></item></item></item></animation-list></code>

在animation-list中,item的先後順序就是圖片在動畫中播放的順序。順序設定好了以後,我們就將該anim綁定到我們的ImageView上,然後進行播放。

    

最後就是我們的代碼,擷取到該drawable,然後進行播放。

    ImageView iv_animaView = (ImageView) findViewById(R.id.iv_frame);    AnimationDrawable animationDrawable = (AnimationDrawable) iv_animaView.getBackground();    animationDrawable.start();

看下動畫效果:

是不是很簡單,簡單的幾行代碼就可以做出一個gif的動畫效果,不過現在貌似有開原始檔控制可以載入gif圖片,回頭研究下看看二者的效率如何。

2、裸奔的機器人
  通過前面的一個案例,我們已經基本熟悉了Frame Animation的使用,下面我們在做一個例子,來鞏固下知識點。

代碼實現

<code class=" hljs xml"><animation-list xmlns:android="http://schemas.android.com/apk/res/android">        <item android:drawable="@mipmap/zzlx1" android:duration="200">        <item android:drawable="@mipmap/zzlx2" android:duration="200">        <item android:drawable="@mipmap/zzlx3" android:duration="200">        <item android:drawable="@mipmap/zzlx4" android:duration="200">        <item android:drawable="@mipmap/zzlx5" android:duration="200">        <item android:drawable="@mipmap/zzlx6" android:duration="200">        <item android:drawable="@mipmap/zzlx7" android:duration="200">        <item android:drawable="@mipmap/zzlx8" android:duration="200">    </item></item></item></item></item></item></item></item></animation-list></code>

後面的代碼同上面,我們只需要看看我們的實現效果即可。

三、幀動畫原理分析
  在上面的開發中,我們在將backgroud對應的Drawable對象轉換為一個AnimationDrawable對象,然後由這個對象啟動Frame動畫,那麼這個類究竟是由何方神聖呢?讓我們一起look look。

1、AnimationDrawable概述
  AnimationDrawable用於建立frame-by-frame(逐幀)動畫,它定義了一些列的Drawable對象可用於設定View的backgroud背景屬性。frame-by-frame動畫最簡單的方式是通過XML檔案進行建立,然後將xml檔案放到res/drawable/folder檔案夾下,同時將此drawa對象設定到view的backgroud屬性。Xml檔案的組成:

animation-list:根節點,包含一系列的item item:每個item對應一個frame(幀)

下面是在代碼中建立和使用幀動畫:

    ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image);    img.setBackgroundResource(R.drawable.spin_animation);    AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();    frameAnimation.start();

我們在來看看AnimationDrawable對象給我們提供的屬性。

AnimationDrawable_visible:設定是否可見 AnimationDrawable_variablePadding: AnimationDrawable_oneshot:設定是否只播放一次,true是,false否 AnimationDrawableItem_duration:設定每幀動畫之間的時間間隔 AnimationDrawableItem_drawable:設定每幀之間間隔的drawable對象

2、AnimationDrawable源碼分析
  上面我們已經對AnimationDrawable進行了一個簡要的分析,瞭解了一些它的屬性,我們心中擷取對幀動畫還有一些疑惑,比如一些問題:

AnimationDrawable是如何形成一個個幀畫面? Frame Animation是如何?不斷迴圈播放? 我們能否通過代碼控制Frame動畫的播放?

下面我們就圍繞上面的幾個問題對AnimationdDrawable進行分析。查看源碼,我們可以看到AnimationDrawable暴露的public方法。

  AnimationDrawable根據名稱,我們也能推算到這是一個Drawable的子類,我們仔細一想,為什麼通過getBackgroud()方法獲得的Drawable對象可以轉換到AnimationDrawable這個子類呢?這就需要我們去看下源碼。在Drawable類中,有一個方法createFromXml()方法:<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> /** * Create a drawable from an XML document. For more information on how to * create resources in XML, see * Drawable Resources. */ public static Drawable createFromXml(Resources r, XmlPullParser parser) throws XmlPullParserException, IOException { return createFromXml(r, parser, null); }

這個方法就是用於將我的XML檔案轉換成一個drawable對象,我們接著深入下去,看下createFromXml()這個方法。

    public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)            throws XmlPullParserException, IOException {        AttributeSet attrs = Xml.asAttributeSet(parser);        int type;        while ((type=parser.next()) != XmlPullParser.START_TAG &&                type != XmlPullParser.END_DOCUMENT) {            // Empty loop        }        if (type != XmlPullParser.START_TAG) {            throw new XmlPullParserException("No start tag found");        }        Drawable drawable = createFromXmlInner(r, parser, attrs, theme);        if (drawable == null) {            throw new RuntimeException("Unknown initial tag: " + parser.getName());        }        return drawable;    }

  在這裡我們看到了XML檔案轉換成Drawable的內部,在Android系統中,同樣是通過XmlPullParser進行Xml檔案的解析,在上面的方法中,首先進行xml檔案的開始標籤和結束標籤,判斷xml檔案內部是否為空白節點。然後通過:

    Drawable drawable = createFromXmlInner(r, parser, attrs, theme);

進行XML檔案解析,最後轉換成Drawable對象。

    public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,            Theme theme) throws XmlPullParserException, IOException {        final Drawable drawable;        final String name = parser.getName();        switch (name) {            case "selector":                drawable = new StateListDrawable();                break;            case "animated-selector":                drawable = new AnimatedStateListDrawable();                break;            case "level-list":                drawable = new LevelListDrawable();                break;            case "layer-list":                drawable = new LayerDrawable();                break;            case "transition":                drawable = new TransitionDrawable();                break;            case "ripple":                drawable = new RippleDrawable();                break;            case "color":                drawable = new ColorDrawable();                break;            case "shape":                drawable = new GradientDrawable();                break;            case "vector":                drawable = new VectorDrawable();                break;            case "animated-vector":                drawable = new AnimatedVectorDrawable();                break;            case "scale":                drawable = new ScaleDrawable();                break;            case "clip":                drawable = new ClipDrawable();                break;            case "rotate":                drawable = new RotateDrawable();                break;            case "animated-rotate":                drawable = new AnimatedRotateDrawable();                break;            case "animation-list":                drawable = new AnimationDrawable();                break;            case "inset":                drawable = new InsetDrawable();                break;            case "bitmap":                drawable = new BitmapDrawable();                break;            case "nine-patch":                drawable = new NinePatchDrawable();                break;            default:                throw new XmlPullParserException(parser.getPositionDescription() +                        ": invalid drawable tag " + name);        }        drawable.inflate(r, parser, attrs, theme);        return drawable;    }

在createFromXmlInner方法中,首先擷取我們都xml檔案的標籤,然後根絕我們對應的標籤名稱建立對應的drawable對象,比如我們這次建立的AnimationDrawable對象。然後調用inflater()方法,由於AnimationDrawable方法中已經對inflater方法進行了重寫,所以此時這個就是:

    @Override    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)            throws XmlPullParserException, IOException {        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.AnimationDrawable);        super.inflateWithAttributes(r, parser, a, R.styleable.AnimationDrawable_visible);        updateStateFromTypedArray(a);        a.recycle();        inflateChildElements(r, parser, attrs, theme);        setFrame(0, true, false);    }

至此,我們已經基本理清了從XML檔案到Drawable對象的轉換流程,現在我們就開始分析animation-list節點下的節點如何形成一個個幀動畫效果的。在進行分析之前,我們先瞭解下AnimationState類。這個類用於儲存我們的一系列drawable。通過源碼發現:

    private final static class AnimationState extends DrawableContainerState

這個類繼承DrawableContainerState類,DrawableContainerState中有一個成員變數Drawable[] mDrawables;用於儲存我們的drawable資訊。明白這一點,我們就可以分析方法inflateChildElements方法。

    private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,            Theme theme) throws XmlPullParserException, IOException {        int type;        final int innerDepth = parser.getDepth()+1;        int depth;        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT                && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {            if (type != XmlPullParser.START_TAG) {                continue;            }            if (depth > innerDepth || !parser.getName().equals("item")) {                continue;            }            final TypedArray a = obtainAttributes(r, theme, attrs,                    R.styleable.AnimationDrawableItem);            final int duration = a.getInt(R.styleable.AnimationDrawableItem_duration, -1);            if (duration < 0) {                throw new XmlPullParserException(parser.getPositionDescription()                        + ":  tag requires a 'duration' attribute");            }            Drawable dr = a.getDrawable(R.styleable.AnimationDrawableItem_drawable);            a.recycle();            if (dr == null) {                while ((type=parser.next()) == XmlPullParser.TEXT) {                    // Empty                }                if (type != XmlPullParser.START_TAG) {                    throw new XmlPullParserException(parser.getPositionDescription()                            + ":  tag requires a 'drawable' attribute or child tag"                            + " defining a drawable");                }                dr = Drawable.createFromXmlInner(r, parser, attrs, theme);            }            mAnimationState.addFrame(dr, duration);            if (dr != null) {                dr.setCallback(this);            }        }    }

在這方法裡面通過TypeArray擷取drawable的相關資訊,然後調用mAnimationState的addFrame方法,將一系列動畫資訊就儲存在drawable數組中。

  通過上面的分析,一系列的動畫已經轉出並進行了儲存,我們接下來的任務就是進行start的分析,分析動畫的開啟。

    public void start() {        mAnimating = true;        if (!isRunning()) {            // Start from 0th frame.            setFrame(0, false, mAnimationState.getChildCount() > 1                    || !mAnimationState.mOneShot);        }    }

通過setFrame方法設定我們的drawable,裡面有selectDrawable(frame)進行設定。

基本的流程就是這個樣子,主要理解使用即可。後面會接著分析幾篇關於動畫的文章。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.