Android中的幀動畫
- 原文鏈接 : Frame Animations in Android
- 譯文出自 : 開發技術前線 www.devtf.cn
- 譯者 : jianghejie
- 校對 : LangleyChang </ul>
動畫可以為你的app注入活力與個性。讓我們來看看實現動畫的一個子類:幀動畫(Frame Animation),從名字上可以看出,這種動畫是一幀一幀的繪制出來的。
在谷歌的Material Design官方專題中,花了整整一頁來介紹 Delightful Details ,其中有幀動畫的絕佳例子。
真是精美的動畫!不幸的是,這個頁面上沒有丁點關于如何實現這種效果的參考鏈接,所以我就來幫忙了!我們將講解一遍如何制作空心心形到實心心形的過渡動畫,然后講解與之反向的動畫。效果如下:
圖片序列
幀動畫的原理很簡單:就像老式電影膠卷那樣,快速掠過一些列的圖片,“幀”其實就是一張圖片,因此創建一個自定義幀動畫的第一步就是建立圖片序列。
我們有兩種選擇:使用xml的drawable(比如shape drawable)或者是使用實際的圖片。簡便起見,我們直接使用下面的一些列PNG圖片:
在產品級的應用中,我們還需要保證圖片尺寸可以適配不同的屏幕分辨率。但是現在,我們將所有的圖片都扔到res/drawable-mdpi目錄下完事。我推薦圖片的命名采用自描述的方式,比如ic_heart_0.png, ic_heart_1.png以此類推。。。這樣我們就不需要查看圖片就知道圖片的順序。
但誰叫我是屌絲呢,我選擇將圖片按照填充的百分比程度命名。
XML Drawable
現在我們已經有了要掠一遍的圖片,下一步就是為動畫定義一個XML的Drawable,我們又遇到了兩種選擇:Animation-list和Animated-selector。
Animation-List
Animation-list是幀動畫的默認選擇,因為在API 1的時候就有了,同時它非常簡單。就是簡單的掠過指定順序和持續時間的圖片序列。
這里是填充到實心效果的Animation-list的例子,在res/drawable/animation_list_filling.xml中:
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true"> <item android:duration="500" android:drawable="@drawable/ic_heart_0"/> <item android:duration="500" android:drawable="@drawable/ic_heart_25"/> <item android:duration="500" android:drawable="@drawable/ic_heart_50"/> <item android:duration="500" android:drawable="@drawable/ic_heart_75"/> <item android:duration="500" android:drawable="@drawable/ic_heart_100"/> </animation-list>
列表中的每一個item都指向圖片序列中的一張圖片。我們只需把它們的順序擺放正確并且添加一個毫秒為單位的持續時間即可。
下面是實現變為空心效果的Animation-list,在res/drawable/animation_list_emptying.xml中:
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true"> <item android:duration="500" android:drawable="@drawable/ic_heart_100"/> <item android:duration="500" android:drawable="@drawable/ic_heart_75"/> <item android:duration="500" android:drawable="@drawable/ic_heart_50"/> <item android:duration="500" android:drawable="@drawable/ic_heart_25"/> <item android:duration="500" android:drawable="@drawable/ic_heart_0"/> </animation-list>
你可能注意到了,在兩個代碼片段中都有android:oneshot=”true”,這是animation-list的一個屬性,表示播放完一次動畫之后便停止動畫。如果這個屬性值設置為“false”,則動畫會重復播放。
在實際產品中,500毫秒時間太長,但是作為演示,我有意夸大了這個時間。還有一點,5幀圖片對于產生流暢的過渡來說還是不夠多。使用多少幀以及每幀的顯示時間取決于個人。作為參考,我覺得15毫秒的15幀圖片就可以非常流暢了。
Animated-Selector
Animated-selector要稍微復雜一些,因為它是基于狀態的。根據View的狀態(比如選中與激活狀態),selector將使用提供的Transition來過渡到正確的狀態。Animated-selector只在Lollipop上有效,因此我們需要在-v21 package中也定義一個xml。
下面是一個Animated-selector的例子,放在res/drawable-v21/selector.xml中:
<?xml version="1.0" encoding="utf-8"?> <animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/on" android:state_activated="true"> <bitmap android:src="@drawable/ic_heart_100"/> </item> <item android:id="@+id/off"> <bitmap android:src="@drawable/ic_heart_0"/> </item> <transition android:fromId="@+id/on" android:toId="@+id/off" android:drawable="@drawable/animation_emptying"> </transition> <transition android:fromId="@id/off" android:toId="@id/on" android:drawable="@drawable/animation_filling"> </transition> </animated-selector>
仔細觀察它是如何將前面定義的Animation-list引用為Transition的。
這個animated-selector沒有任何問題,但是我們需要考慮非Lollipop設備。我們在res/drawable/selector.xml中定義一個沒有動畫的selector:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_activated="true"> <bitmap android:src="@drawable/ic_heart_100"/> </item> <item android:state_activated="false"> <bitmap android:src="@drawable/ic_heart_0"/> </item> </selector>
現在我們的selector在任意設備上都可以工作。因為我們只使用了一般的selector,如果在Lollipop以前的設備上,animated-selector將直接跳過過渡動畫,直接到結束狀態。當然Lollipop設備是有我們在animated-selector中定義的過渡效果的。
在上面的代碼片段中,animated-selector只關注了android:state_activated屬性。就如一般的selector一樣,我為不同的狀態定義了不同的item,但不同的是,我們還定義了不同狀態間動畫過渡的transition。在這個例子中,我直接將transition指向前面定義好了的animation-list。
現在我們有了四個xml文件:一個充到實心效果的xml,實心到空心的xml,兩個在空心實心之間切換的xml。
設置ImageView
現在可以設置一些圖片來玩了。我們這里有三個ImageView,分別對應前面定義的三個XML Drawable。將下面的代碼放到你的Activity的布局中:
<ImageView android:id="@+id/imageview_animation_list_filling" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/animation_list_filling" /> <ImageView android:id="@+id/imageview_animation_list_emptying" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/animation_list_emptying" /> <ImageView android:id="@+id/imageview_animated_selector" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/selector" />
這只是幾個id唯一,背景為我們定義的xml Drawable的ImageView。
開始動畫
開始動畫的方式在兩種實現方法中是不一樣的,我們先從animation-list開始:
Animation-List
在Activity中,我們得到ImageView的引用,然后開始動畫。如下:
ImageView mImageViewFilling = (ImageView) findViewById(R.id.imageview_animation_list_filling); ((AnimationDrawable) mImageViewFilling.getBackground()).start();
下面是效果:
接下來是它的搭檔-反向過程(除了id都是一樣的)
ImageView mImageViewEmptying = (ImageView) findViewById(R.id.imageview_animation_list_emptying); ((AnimationDrawable) mImageViewEmptying.getBackground()).start();
效果如下:
這些代碼可以放在onCreate(在Activity開始的時候自動開始)或者一個OnClickListener(等待用戶觸發)中,取決于你自己!
Animated-Selector
當使用Animated-selector的時候,動畫將在狀態條件滿足selector的時候被觸發。在我們這個簡單的例子中,我們在Activity的onCreate方法中為ImageView添加一個click listener:
mImageViewSelector.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mImageViewSelector.setActivated(!mImageViewSelector.isActivated()); } });
當用戶點擊心形,它將會根據當前的狀態在實心與空心之間切換,下面是我的心形循環顯示的gif圖:
Delightful Details
Frame Animations have the power to surprise and delight users, plus it’s fun to add little personal touches to an app. Go forth and animate!幀動畫可以取悅你的用戶,為app增添個性,搞起!