Android中的View動畫和屬性動畫

概述

在Android開發(fā)的過程中紊浩,View的變化是很常見的窖铡,如果View變化的過程沒有動畫來過渡而是瞬間完成,會讓用戶感覺很不友好坊谁,因此學習好Android系統(tǒng)中的動畫框架是很重要的础废。
Android系統(tǒng)提供了兩個動畫框架:屬性動畫框架和View動畫框架谈截。 兩個動畫框架都是可行的選項,但是屬性動畫框架通常是首選的使用方法,因為它更靈活媒殉,并提供更多的功能。 除了這兩個框架摘盆,還可以使用Drawable動畫(即逐幀動畫坛梁,AnimationDrawable),它允許你加載Drawable資源并逐幀地顯示它們膘融。
1> View動畫框架
View動畫框架是舊的框架芙粱,只能用于Views。 比較容易設置和能滿足許多應用程序的需要氧映。View動畫框架中一共提供了AlphaAnimation(透明度動畫)春畔、RotateAnimation(旋轉動畫)、ScaleAnimation(縮放動畫)岛都、TranslateAnimation(平移動畫)四種類型的補間動畫律姨;并且View動畫框架還提供了動畫集合類(AnimationSet),通過動畫集合類(AnimationSet)可以將多個補間動畫以組合的形式顯示出來臼疫。View動畫框架中動畫相關類的繼承關系如下圖所示:


Animation類和其子類

2> 屬性動畫框架
與屬性動畫相比View動畫存在一個缺陷择份,View動畫改變的只是View的顯示,而沒有改變View的響應區(qū)域烫堤,并且View動畫只能對View做四種類型的補間動畫荣赶。因此Google在Android3.0(API級別11)及其后續(xù)版本中添加了屬性動畫框架凤价,從名稱中就可以知道只要某個類具有屬性(即該類含有某個字段的set和get方法),那么屬性動畫框架就可以對該類的對象進行動畫操作(其實就是通過反射技術來獲取和執(zhí)行屬性的get拔创,set方法)利诺,同樣屬性動畫框架還提供了動畫集合類(AnimatorSet),通過動畫集合類(AnimatorSet)可以將多個屬性動畫以組合的形式顯示出來剩燥。屬性動畫框架中動畫相關類的繼承關系如下圖所示:

Animator類及其子類

3> Drawable 動畫
可繪制動畫通過一個接一個地加載一系列Drawable資源來創(chuàng)建動畫慢逾。 這是一個傳統(tǒng)的動畫,它是用一系列不同的圖像創(chuàng)建的灭红,按順序播放侣滩,就像一卷電影;逐幀動畫中動畫相關類的繼承關系如下圖所示:


逐幀動畫(AnimationDrawable)

預備知識

1. 時間插值器

對于補間動畫:時間插值器(TimeInterpolator)的作用是根據時間流逝的百分比計算出動畫進度的百分比比伏。有了動畫進度的百分比胜卤,就可以很容易的計算出動畫開始的關鍵幀與將要顯示的幀之間的差異(通過Transformation類的對象表示),下面展示TranslateAnimation類中如何根據動畫進度的百分比計算出動畫開始的關鍵幀與將要顯示的幀之間的差異:

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    float dx = mFromXDelta;
    float dy = mFromYDelta;
    if (mFromXDelta != mToXDelta) {
        dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
    }
    if (mFromYDelta != mToYDelta) {
        dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
    }
    t.getMatrix().setTranslate(dx, dy);
}

上面源碼中applyTransformation方法的第一個參數就是通過時間插值器(TimeInterpolator)獲取的動畫進度的百分比赁项。
然后根據幀之間的差異繪制出將要顯示的幀葛躏,以此類推從而形成動畫的效果。

對于屬性動畫:時間插值器(TimeInterpolator)的作用是根據時間流逝的百分比計算出動畫進度的百分比(即屬性值改變的百分比)悠菜。

Android提供了常用的時間插值器如下表所示:

Interpolator class Resource ID
AccelerateDecelerateInterpolator @android:anim/accelerate_decelerate_interpolator
AccelerateInterpolator @android:anim/accelerate_interpolator
AnticipateInterpolator @android:anim/anticipate_interpolator
AnticipateOvershootInterpolator @android:anim/anticipate_overshoot_interpolator
BounceInterpolator @android:anim/bounce_interpolator
CycleInterpolator @android:anim/cycle_interpolator
DecelerateInterpolator @android:anim/decelerate_interpolator
LinearInterpolator @android:anim/linear_interpolator
OvershootInterpolator @android:anim/overshoot_interpolator

上面列舉的常用的時間插值器對應的類與時間插值器(TimeInterpolator)之間的繼承關系如下圖所示:



為了比較直觀的感受到Android提供的常用時間插值器的效果舰攒,我通過程序繪制出了動畫進度的百分比隨著時間流逝的百分比變化的波形圖(波形圖的橫向代表時間流逝的百分比,縱向代表動畫進度的百分比悔醋,時間流逝的百分比我一共選取了11個值摩窃,分別為0、0.1芬骄、以此類推一直到1)猾愿,如下所示:

AccelerateDecelerateInterpolator
AccelerateInterpolator
AnticipateInterpolator
AnticipateOvershootInterpolator
BounceInterpolator
CycleInterpolator
DecelerateInterpolator
LinearInterpolator
OvershootInterpolator

2. 類型估值器

類型估值器(TypeEvaluator)是針對于屬性動畫框架的,對于View動畫框架是不需要類型估值器(TypeEvaluator)的账阻。
類型估值器(TypeEvaluator)的作用是根據屬性值改變的百分比計算出改變后的屬性值蒂秘。由于屬性動畫實際上作用的是對象的屬性,而屬性的類型是不同的淘太,因此Android內置了一些常用的類型估值器來操作不同類型的屬性姻僧,如下圖所示:


TypeEvaluator及其子類

View動畫框架

使用View動畫框架可以在Views上執(zhí)行補間動畫。 補間動畫是指只要指定動畫的開始蒲牧、動畫結束的"關鍵幀"撇贺,而動畫變化的"中間幀"由系統(tǒng)計算并補齊;無論動畫怎樣改變View的顯示區(qū)域(移動或者改變大斜馈)松嘶,持有該動畫的View的邊界不會自動調整來適應View的顯示區(qū)域, 即使如此挎扰,該動畫仍將被繪制在超出其視圖邊界并且不會被剪裁翠订, 但是缓升,如果動畫超過父視圖的邊界,則會出現裁剪蕴轨。在Android中的View動畫框架中一共提供了AlphaAnimation(透明度動畫)、RotateAnimation(旋轉動畫)骇吭、ScaleAnimation(縮放動畫)橙弱、TranslateAnimation(平移動畫)四種類型的補間動畫。

FILE LOCATION:
    res/anim/filename.xml
    The filename will be used as the resource ID.
COMPILED RESOURCE DATATYPE:
    Resource pointer to an Animation.
RESOURCE REFERENCE:
    In Java: R.anim.filename
    In XML: @[package:]anim/filename

語法:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:shareInterpolator=["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
    <set>
        ...
    </set>
</set>

<set>標簽表示補間動畫的集合燥狰,對應于AnimationSet類棘脐,所以上面語法中的<set>標簽可以包含多個補間動畫的標簽;并且補間動畫的集合中還可以包含補間動畫的集合龙致。
<set>標簽的相關屬性如下所示:

android:interpolator
Interpolator resource. 設置動畫集合所采用的插值器蛀缝,默認值為@android:anim/accelerate_decelerate_interpolator

android:shareInterpolator
Boolean. 表示集合中的動畫是否共享集合的插值器。當值為true且集合沒有設置插值器目代,
此時集合中的動畫就會使用默認的插值器@android:anim/accelerate_decelerate_interpolator屈梁,
但是你也可以為集合中的動畫單獨指定所需的插值器。

AlphaAnimation(透明度動畫)

上面語法中的<alpha>標簽代表的就是透明度動畫榛了,顧名思義透明度動畫就是通過不斷改變View的透明度實現動畫的效果在讶。
<alpha>標簽相關屬性如下所示:

android:fromAlpha
Float. 設置透明度的初始值,其中0.0是透明霜大,1.0是不透明的构哺。

android:toAlpha
Float. 設置透明度的結束值,其中0.0是透明战坤,1.0是不透明的曙强。

舉例如下:
首先展示一下動畫效果:



實現代碼如下:
首先通過xml定義一個透明度動畫(AlphaAnimation),代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <alpha
        android:duration="2000"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
</set>

接著將上面定義的透明度動畫(AlphaAnimation)設置為ImageView的src途茫,然后通過startAnimation播放動畫碟嘴,代碼如下:

<ImageView
    android:id="@+id/image"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_gravity="center"
    android:src="@drawable/second_pic"
    android:scaleType="centerCrop"
    android:alpha="1.0" >
</ImageView>

loadAnimation = AnimationUtils.loadAnimation(getActivity(),
        R.anim.base_animation_alpha);
loadAnimation.setFillAfter(true);
image.startAnimation(loadAnimation);

ScaleAnimation(縮放動畫)

上面語法中的<scale>標簽代表的就是縮放動畫,顧名思義縮放動畫就是通過不斷縮放View的寬高實現動畫的效果慈省。
<scale>標簽相關屬性如下所示:

android:fromXScale
Float. 水平方向縮放比例的初始值臀防,其中1.0是沒有任何變化。
android:toXScale
Float. 水平方向縮放比例的結束值边败,其中1.0是沒有任何變化袱衷。
android:fromYScale
Float. 豎直方向縮放比例的初始值,其中1.0是沒有任何變化笑窜。
android:toYScale
Float. 豎直方向縮放比例的結束值致燥,其中1.0是沒有任何變化。
android:pivotX
Float. 縮放中心點的x坐標
android:pivotY
Float. 縮放中心點的y坐標

舉例如下:
首先展示一下動畫效果:



實現代碼如下:
首先通過xml定義一個縮放動畫(ScaleAnimation)排截,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <scale
        android:duration="5000"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:interpolator="@android:anim/accelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0.0"
        android:toYScale="0.0" />
</set>

接著將上面定義的縮放動畫(ScaleAnimation)設置為ImageView的src嫌蚤,然后通過startAnimation播放動畫辐益,代碼如下:

ImageView對應的layout代碼同上,這里就不贅敘了脱吱。

loadAnimation = AnimationUtils.loadAnimation(getActivity(),
        R.anim.base_animation_scale);
loadAnimation.setFillAfter(true);
image.startAnimation(loadAnimation);

TranslateAnimation(平移動畫)

上面語法中的<translate>標簽代表的就是平移動畫智政,顧名思義平移動畫就是通過不斷移動View的位置實現動畫的效果。
<translate>標簽相關屬性如下所示:

android:fromXDelta
Float or percentage. 移動起始點的x坐標. 表示形式有三種:
1 相對于自己的左邊界的距離箱蝠,單位像素值续捂。(例如 "5")
2 相對于自己的左邊界的距離與自身寬度的百分比。(例如  "5%")
3 相對于父View的左邊界的距離與父View寬度的百分比宦搬。(例如 "5%p")
android:toXDelta
Float or percentage. 移動結束點的x坐標. 表現形式同上
android:fromYDelta
Float or percentage. 移動起始點的y坐標. 表示形式有三種:
1 相對于自己的上邊界的距離牙瓢,單位像素值。(例如 "5")
2 相對于自己的上邊界的距離與自身高度的百分比间校。(例如  "5%")
3 相對于父View的上邊界的距離與父View高度的百分比矾克。(例如 "5%p")
android:toYDelta
Float or percentage. 移動結束點的y坐標. 表現形式同上

舉例如下:
首先展示一下動畫效果:


實現代碼如下:
首先通過xml定義一個平移動畫(TranslateAnimation),代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="2000"
        android:fromXDelta="20"
        android:fromYDelta="20"
        android:toXDelta="100"
        android:toYDelta="100" />
</set>

接著將上面定義的平移動畫(TranslateAnimation)設置為ImageView的src憔足,然后通過startAnimation播放動畫胁附,代碼如下:

ImageView對應的layout代碼同上,這里就不贅敘了四瘫。

loadAnimation = AnimationUtils.loadAnimation(getActivity(),
        R.anim.base_aniamtion_translate);
loadAnimation.setFillAfter(true);
image.startAnimation(loadAnimation);

RotateAnimation(旋轉動畫)

上面語法中的<rotate>標簽代表的就是旋轉動畫汉嗽,顧名思義旋轉動畫就是通過不斷旋轉View實現動畫的效果。
<rotate>標簽相關屬性如下所示:

android:fromDegrees
Float. 旋轉初始的角度找蜜。
android:toDegrees
Float. 旋轉結束的角度饼暑。
android:pivotX
Float or percentage. 旋轉中心點x坐標,表示形式有三種:
1 相對于自己的左邊界的距離洗做,單位像素值弓叛。(例如 "5")
2 相對于自己的左邊界的距離與自身寬度的百分比。(例如 "5%")
3 相對于父View的左邊界的距離與父View寬度的百分比诚纸。(例如 "5%p")
android:pivotY
Float or percentage. 旋轉中心點y坐標撰筷,表示形式有三種:
1 相對于自己的上邊界的距離,單位像素值畦徘。(例如 "5")
2 相對于自己的上邊界的距離與自身寬度的百分比毕籽。(例如 "5%")
3 相對于父View的上邊界的距離與父View高度的百分比。(例如 "5%p")

舉例如下:
首先展示一下動畫效果:


實現代碼如下:
首先通過xml定義一個旋轉動畫(RotateAnimation)井辆,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <rotate
        android:duration="1000"
        android:fromDegrees="0"
        android:interpolator="@android:anim/accelerate_decelerate_interpolator"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="+360" />
</set>

接著將上面定義的旋轉動畫(RotateAnimation)設置為ImageView的src关筒,然后通過startAnimation播放動畫,代碼如下:

ImageView對應的layout代碼同上杯缺,這里就不贅敘了蒸播。

loadAnimation = AnimationUtils.loadAnimation(getActivity(),
        R.anim.base_anmation_rotate);
image.startAnimation(loadAnimation);

補間動畫(Tween animation)兩個常用的特殊場景

1 通過布局動畫(LayoutAnimation)給ViewGroup的子View指定入場動畫。
舉例如下:
首先展示一下動畫效果:



實現代碼如下:
首先通過xml定義ViewGroup的子View入場動畫,代碼如下:

res/anim/zoom_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/decelerate_interpolator" >
  <scale
        android:duration="500"
        android:fromXScale="0.1"
        android:fromYScale="0.1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0" />
  <alpha
        android:duration="500"
        android:fromAlpha="0"
        android:toAlpha="1.0" />
</set>

接著為ListView設置布局動畫(LayoutAnimation)袍榆,代碼如下所示:

LayoutAnimationController lac=new LayoutAnimationController(AnimationUtils.loadAnimation(this, R.anim.zoom_in));
lac.setDelay(0.5f);
lac.setOrder(LayoutAnimationController.ORDER_RANDOM);
mListView.setLayoutAnimation(lac);
//mListView.startLayoutAnimation(); //可以通過該方法控制動畫在何時播放胀屿。

上面是通過java代碼來為ListView設置布局動畫(LayoutAnimation)的,其實通過xml也可以為ListView設置布局動畫(LayoutAnimation)包雀,代碼如下所示:

<ListView
    android:id="@+id/listView"
    android:layoutAnimation="@anim/anim_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
</ListView>

res/anim/anim_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.5"
    android:animationOrder="random"
    android:animation="@anim/zoom_in"/>

2 通過補間動畫(Tween animation)為Activity自定義切換動畫
Android系統(tǒng)為Activity設置了默認的切換動畫宿崭,這個動畫我們是可以進行自定義的。通過調用Activity類的overridePendingTransition(int enterAnim, int exitAnim)方法可以實現自定義Activity的切換動畫才写,注意這個方法必須在startActivity和finish調用之后被調用劳曹,否者沒有效果。

Drawable動畫(逐幀動畫)

逐幀動畫是用來逐幀顯示預先定義好的一組圖片琅摩,類似于電影播放。對應于AnimationDrawable類锭硼。

FILE LOCATION:
    res/drawable/filename.xml
    The filename will be used as the resource ID.
COMPILED RESOURCE DATATYPE:
    Resource pointer to an AnimationDrawable.
RESOURCE REFERENCE:
    In Java: R.drawable.filename
    In XML: @[package:]drawable.filename

不同于補間動畫房资,逐幀動畫資源文件放在drawable文件夾下。
語法:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot=["true" | "false"] >
    <item
        android:drawable="@[package:]drawable/drawable_resource_name"
        android:duration="integer" />
</animation-list>

<animation-list>標簽用來包含逐幀動畫的每一幀檀头。
<animation-list>標簽的相關屬性如下所示:

android:oneshot
Boolean. 當設置為true時動畫只會播放一次轰异,否者會循環(huán)播放。

<item>用來表示逐幀動畫的每一幀圖片暑始。
<item>標簽的相關屬性如下所示:

android:drawable
Drawable resource. 設置當前幀對應的Drawable 資源搭独。
android:duration
Integer. 設置顯示該幀的時間, 單位為毫秒(milliseconds)。

舉例如下:
首先展示一下動畫效果:


實現代碼如下:
首先通過xml定義一個逐幀動畫(AnimationDrawable)廊镜,代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true" >
    <item
        android:drawable="@drawable/first_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/second_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/third_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/fourth_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/fifth_pic"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/sixth_pic"
        android:duration="1000"/>
</animation-list>

然后將上面定義的AnimationDrawable作為View的背景并且通過AnimationDrawable來播放動畫牙肝,代碼如下:

image.setImageResource(R.drawable.anim_list);
AnimationDrawable animationDrawable = (AnimationDrawable) image.getDrawable();
animationDrawable.start();
//animationDrawable.stop(); //如果oneshot為false,必要時要停止動畫

上面是通過xml定義一個逐幀動畫嗤朴,也可以完全通過代碼來實現和上面相同效果配椭,如下所示:

//代碼定義、創(chuàng)建雹姊、執(zhí)行動畫
AnimationDrawable animationDrawable = new AnimationDrawable();
animationDrawable.addFrame(getResources().getDrawable(R.drawable.first_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.second_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.third_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.fourth_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.fifth_pic), 1000);
animationDrawable.addFrame(getResources().getDrawable(R.drawable.sixth_pic), 1000);
animationDrawable.setOneShot(true);
image.setImageDrawable(animationDrawable);
animationDrawable.start();

注意:對于一些手機股缸,在Activity的onCreate()方法中調用AnimationDrawable的start()方法不起效果,可能是由于播放動畫的ImageView還沒有準備好吱雏,此時就需要在onCreate之后的生命周期的方法中調用敦姻,比如onResume方法。

屬性動畫框架

與屬性動畫相比View動畫存在一個缺陷歧杏,View動畫改變的只是View的顯示镰惦,而沒有改變View的響應區(qū)域,并且View動畫只能對View做四種類型的補間動畫得滤,因此Google在Android3.0及其后續(xù)版本中添加了屬性動畫框架陨献。同樣屬性動畫框架還提供了動畫集合類(AnimatorSet),通過動畫集合類(AnimatorSet)可以將多個屬性動畫以組合的形式顯示出來懂更。

下面是我關于屬性動畫框架工作原理的總結:
顧名思義只要某個類具有屬性(即該類含有某個字段的set和get方法)眨业,那么屬性動畫框架就可以對該類的對象進行動畫操作(其實就是通過反射技術來獲取和執(zhí)行屬性的get急膀,set方法),因此屬性動畫框架可以實現View動畫框架的所有動畫效果并且還能實現View動畫框架無法實現的動畫效果龄捡。屬性動畫框架工作原理可以總結為如下三步:
1 在創(chuàng)建屬性動畫時如果沒有設置屬性的初始值卓嫂,此時Android系統(tǒng)就會通過該屬性的get方法獲取初始值,所以在沒有設置屬性的初始值時聘殖,必須提供該屬性的get方法晨雳,否者程序會Crash。
2 在動畫播放的過程中奸腺,屬性動畫框架會利用時間流逝的百分比獲取屬性值改變的百分比(即通過時間插值器)餐禁,接著利用獲取的屬性值改變的百分比獲取改變后的屬性值(即通過類型估值器)。
3 通過該屬性的set方法將改變后的屬性值設置到對象中突照。

還要注意一點帮非,雖然通過set方法改變了對象的屬性值,但是還要將這種改變用動畫的形式表現出來讹蘑,否者就不會有動畫效果末盔,所以屬性動畫框架本身只是不斷的改變對象的屬性值并沒有實現動畫效果。

FILE LOCATION:
    res/animator/filename.xml
    The filename will be used as the resource ID.
COMPILED RESOURCE DATATYPE:
    Resource pointer to a ValueAnimator, ObjectAnimator, or AnimatorSet.
RESOURCE REFERENCE:
    In Java: R.animator.filename
    In XML: @[package:]animator/filename

語法:

<set
  android:ordering=["together" | "sequentially"]>

    <objectAnimator
        android:propertyName="string"
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <animator
        android:duration="int"
        android:valueFrom="float | int | color"
        android:valueTo="float | int | color"
        android:startOffset="int"
        android:repeatCount="int"
        android:repeatMode=["repeat" | "reverse"]
        android:valueType=["intType" | "floatType"]/>

    <set>
        ...
    </set>
</set>

在上面的語法中座慰,<set>對應AnimatorSet(屬性動畫集合)類陨舱,<objectAnimator>對應ObjectAnimator類,<animator>標簽對應ValueAnimator類版仔;并且屬性動畫集合還可以包含屬性動畫集合游盲。
<set>標簽相關屬性如下所示:

android:ordering
該屬性有如下兩個可選值:
together:表示動畫集合中的子動畫同時播放。
sequentially:表示動畫集合中的子動畫按照書寫的先后順序依次播放蛮粮。
該屬性的默認值是together背桐。

通過ObjectAnimator實現屬性動畫

<objectAnimator>標簽相關屬性如下所示:

android:propertyName
String. Required. 屬性動畫作用的屬性名稱 
android:duration
int. 表示動畫的周期,默認值為300毫秒
android:valueFrom
float, int, or color. 表示屬性的初始值
android:valueTo
float, int, or color. Required. 表示屬性的結束值
android:startOffset
int. 表示調用start方法后延遲多少毫秒開始播放動畫
android:repeatCount
int. 表示動畫的重復次數蝉揍,-1代表無限循環(huán)链峭,默認值為0,表示動畫只播放一次又沾。
android:repeatMode
表示動畫的重復模式弊仪,該屬性有如下兩個可選值:
repeat:表示連續(xù)重復
reverse:表示逆向重復
android:valueType
Keyword.  表示android:propertyName所指定屬性的類型,有intType和floatType兩個可選項杖刷,
分別代表屬性的類型為整型和浮點型励饵,另外如果屬性是顏色值,那么就不需要指定
android:valueType屬性滑燃,Android系統(tǒng)會自動對顏色類型的屬性做處理役听。
默認值為floatType。

利用ObjectAnimator實現與補間動畫中4個實例相同的動畫效果,代碼如下:

//利用ObjectAnimator實現透明度動畫
ObjectAnimator.ofFloat(mImageView, "alpha", 1, 0, 1)
        .setDuration(2000).start();
//利用AnimatorSet和ObjectAnimator實現縮放動畫
final AnimatorSet animatorSet = new AnimatorSet();
image.setPivotX(image.getWidth()/2);
image.setPivotY(image.getHeight()/2);
animatorSet.playTogether(
        ObjectAnimator.ofFloat(image, "scaleX", 1, 0).setDuration(5000),
        ObjectAnimator.ofFloat(image, "scaleY", 1, 0).setDuration(5000));
animatorSet.start();
//利用AnimatorSet和ObjectAnimator實現平移動畫
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
        ObjectAnimator.ofFloat(image, "translationX", 20, 100).setDuration(2000),
        ObjectAnimator.ofFloat(image, "translationY", 20, 100).setDuration(2000));
animatorSet.start();
//利用ObjectAnimator實現旋轉動畫
image.setPivotX(image.getWidth()/2);
image.setPivotY(image.getHeight()/2);
ObjectAnimator.ofFloat(image, "rotation", 0, 360)
        .setDuration(1000).start();

上面是通過代碼的形式實現的屬性動畫典予,對于通過xml定義屬性動畫的方式不是很常用甜滨,因為屬性的起始和結束值大多是在程序運行的時候動態(tài)獲取的。大家可以根據上面的語法自己嘗試一下瘤袖。

通過ValueAnimator實現屬性動畫

<animator>標簽相關屬性如下所示:

與<objectAnimator>標簽相比衣摩,除了沒有android:propertyName屬性和valueFrom 屬性是Required的之外,
其他的屬性都相同并且屬性的作用也一樣捂敌。

其實ValueAnimator類就是一個數值生成器艾扮,也就是沒有上面關于屬性動畫框架工作原理的第1步和第3步,ObjectAnimator作為ValueAnimator的子類占婉,實現了這兩步泡嘴。你只要給ValueAnimator提供一個初始值、結束值和周期時間逆济,ValueAnimator就會按照屬性動畫框架工作原理的第2步中的步驟生成具有一定規(guī)則的數字磕诊。

無法使用屬性動畫或者屬性動畫不起作用的情況和解決方法

無法使用屬性動畫或者屬性動畫不起作用的情況如下:
1 該字段沒有沒有set和get方法
2 該屬性的set方法僅僅改變了對象的屬性值,但是沒有將這種改變用動畫的形式表現出來
解決方法如下:
1 如果你又權限的話纹腌,給這個字段添加get和set方法,比如在自定義View中滞磺。
2 使用一個包裝類來封裝該字段對應的類升薯,間接為該字段提供get和set方法。
例如使ImageView從當前高度變化到600击困,代碼如下所示:

ViewWrapper viewWrapper = new ViewWrapper(mImageView);
ObjectAnimator.ofInt(viewWrapper, "height", 600).setDuration(5000).start();

public class ViewWrapper {

    private View view;

    public ViewWrapper(View view) {
        this.view = view;
    }

    public int getHeight(){
        return view.getLayoutParams().height;
    }

    public void setHeight(int height){
        view.getLayoutParams().height = height;
        view.requestLayout();
    }
}

3 通過ValueAnimator實現動畫效果
舉例如下:
首先展示一下運行的效果:



實現代碼如下:

if (mBtHiddenView.getVisibility() == View.GONE) {
    animateOPen(mBtHiddenView);
} else {
    animateClose(mBtHiddenView);
}


private void animateOPen(final View view) {
    view.setVisibility(View.VISIBLE);
    ValueAnimator valueAnimator = createDropAnimator(view, 0, mHiddenViewHeight);
    valueAnimator.setInterpolator(new AccelerateInterpolator());
    valueAnimator.setDuration(1000).start();
}

private void animateClose(final View view) {
    ValueAnimator valueAnimator = createDropAnimator(view, mHiddenViewHeight, 0);
    valueAnimator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            // TODO Auto-generated method stub
            super.onAnimationEnd(animation);
            view.setVisibility(View.GONE);
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(1000).start();
}

private ValueAnimator createDropAnimator(final View view, int start, int end) {
    ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // TODO Auto-generated method stub
            LayoutParams params = view.getLayoutParams();
            params.height = (int) animation.getAnimatedValue();
            view.setLayoutParams(params);
        }
    });
    return valueAnimator;
}

屬性動畫常用的特殊場景

1> 屬性動畫可以為ViewGroup的子View的顯示和隱藏設置過渡動畫涎劈。Android系統(tǒng)中已經提供了默認過渡動畫(在layout文件中將ViewGroup的animateLayoutChanges屬性打開就可以使用系統(tǒng)提供的默認過渡動畫)。Android系統(tǒng)中一共提供了如下所示的4種類型的過渡動畫:

APPEARING 
當通過 設置子View的可見性為VISIBLE或者通過addView方法添加子View 來顯示子View時阅茶,
子View就會執(zhí)行該類型的動畫蛛枚。
該類型動畫的周期為300毫秒,默認延遲為300毫秒脸哀。
DISAPPEARING
當通過 設置子View的可見性為GONE或者通過removeView方法移除子View 來隱藏子View時蹦浦,
子View就會執(zhí)行該類型的動畫。
該類型動畫的周期為300毫秒撞蜂,默認延遲為0毫秒盲镶。
CHANGE_APPEARING
當顯示子View時,所有的兄弟View就會立即依次執(zhí)行該類型動畫并且兄弟View之間執(zhí)行動畫的間隙默認為0毫秒蝌诡,然后才會執(zhí)行顯示子View的動畫溉贿。
該類型動畫的周期為300毫秒,默認延遲為0毫秒浦旱。
CHANGE_DISAPPEARING
當隱藏子View的動畫執(zhí)行完畢后宇色,所有的兄弟View就會依次執(zhí)行該類型動畫并且兄弟View之間執(zhí)行動畫的間隙默認為0毫秒。
該類型動畫的周期為300毫秒,默認延遲為300毫秒宣蠕。

注意 上面描述的都是系統(tǒng)默認的行為例隆,我們可以做適當的改變。

如果感覺Android系統(tǒng)提供的過渡動畫不夠炫植影,你也可以自定義過渡動畫裳擎,下面就舉例說明一下如何自定義過渡動畫,首先展示一下效果圖:


布局相關代碼如下所示:

<?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="match_parent"
android:orientation="vertical">
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
    <Button
        android:id="@+id/btn_add_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加圖片" />
    <Button
        android:id="@+id/btn_remove_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="移除圖片" />
</LinearLayout>
<LinearLayout
    android:id="@+id/ll_image"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="24dp"
    android:gravity="center_horizontal"
    android:animateLayoutChanges="true"
    android:orientation="vertical"/>
</LinearLayout>

在java代碼中為id為ll_image的LinearLayout設置自定義的過渡動畫思币,如下所示:

llImageView = (LinearLayout) root.findViewById(R.id.ll_image);

LayoutTransition transition = new LayoutTransition();

transition.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
transition.setDuration(LayoutTransition.CHANGE_APPEARING, transition.getDuration(LayoutTransition.CHANGE_APPEARING));
transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, 0);

ObjectAnimator appearingAnimator = ObjectAnimator
        .ofPropertyValuesHolder(
                (Object) null,
                PropertyValuesHolder.ofFloat("scaleX", 0.0f, 1.0f),
                PropertyValuesHolder.ofFloat("scaleY", 0.0f, 1.0f),
                PropertyValuesHolder.ofFloat("alpha", 0, 1.0f));
transition.setAnimator(LayoutTransition.APPEARING, appearingAnimator);
transition.setDuration(LayoutTransition.APPEARING, transition.getDuration(LayoutTransition.APPEARING));
transition.setStartDelay(LayoutTransition.APPEARING, transition.getDuration(LayoutTransition.CHANGE_APPEARING));

ObjectAnimator disappearingAnimator = ObjectAnimator
        .ofPropertyValuesHolder(
                (Object) null,
                PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f),
                PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.0f),
                PropertyValuesHolder.ofFloat("alpha", 1.0f, 0));
transition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnimator);
transition.setDuration(LayoutTransition.DISAPPEARING, transition.getDuration(LayoutTransition.DISAPPEARING));
transition.setStartDelay(LayoutTransition.DISAPPEARING, 0);

transition.setStagger(LayoutTransition.CHANGE_DISAPPEARING, 30);
transition.setDuration(LayoutTransition.CHANGE_DISAPPEARING, transition.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, transition.getDuration(LayoutTransition.DISAPPEARING));

llImageView.setLayoutTransition(transition);

上面代碼依次對CHANGE_APPEARING鹿响、APPEARING、DISAPPEARING和CHANGE_DISAPPEARING類型的過渡動畫進行了設置谷饿,下面就來分析常用的設置方法:

setStagger方法
當多個子View要執(zhí)行同一個類型的動畫時惶我,就可以通過該方法來設置子View之間執(zhí)行動畫的間隙,
默認為0毫秒博投。

setAnimator方法
為指定類型的過渡動畫設置自定義的屬性動畫绸贡。

setDuration方法
為指定類型的過渡動畫設置執(zhí)行動畫的周期,默認為300毫秒毅哗。

setStartDelay方法
為指定類型的過渡動畫設置延遲執(zhí)行的時間听怕,默認與過渡動畫的類型相關,上面已經說過虑绵。

setLayoutTransition方法
為ViewGroup設置過渡動畫尿瞭。

經過上面代碼的設置后,LinearLayout顯示或者隱藏子View時就會執(zhí)行相關的過渡動畫翅睛,顯示或者隱藏子View的代碼如下所示:

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btn_add_image:{
            ImageView imageView = new ImageView(getContext());
            imageView.setImageResource(R.drawable.second_pic);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(200,200);
            llImageView.addView(imageView, 0, layoutParams);
        }
            break;
        case R.id.btn_remove_image:{
            int count = llImageView.getChildCount();
            if (count > 0) {
                llImageView.removeViewAt(0);
            }
        }
            break;
    }
}

2> Vector(矢量圖)中的動畫
可以參考我的另外一篇blog Android中的矢量圖

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末声搁,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子捕发,更是在濱河造成了極大的恐慌疏旨,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扎酷,死亡現場離奇詭異檐涝,居然都是意外死亡,警方通過查閱死者的電腦和手機法挨,發(fā)現死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門骤铃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坷剧,你說我怎么就攤上這事惰爬。” “怎么了惫企?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵撕瞧,是天一觀的道長陵叽。 經常有香客問我,道長丛版,這世上最難降的妖魔是什么巩掺? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮页畦,結果婚禮上胖替,老公的妹妹穿的比我還像新娘。我一直安慰自己豫缨,他們只是感情好独令,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著好芭,像睡著了一般燃箭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舍败,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天招狸,我揣著相機與錄音,去河邊找鬼邻薯。 笑死裙戏,一個胖子當著我的面吹牛,可吹牛的內容都是我干的厕诡。 我是一名探鬼主播累榜,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼木人!你這毒婦竟也來了?” 一聲冷哼從身側響起冀偶,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤醒第,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后进鸠,有當地人在樹林里發(fā)現了一具尸體稠曼,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年客年,在試婚紗的時候發(fā)現自己被綠了霞幅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡量瓜,死狀恐怖司恳,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情绍傲,我是刑警寧澤扔傅,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布耍共,位于F島的核電站,受9級特大地震影響猎塞,放射性物質發(fā)生泄漏试读。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一荠耽、第九天 我趴在偏房一處隱蔽的房頂上張望钩骇。 院中可真熱鬧,春花似錦铝量、人聲如沸倘屹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唐瀑。三九已至,卻和暖如春插爹,著一層夾襖步出監(jiān)牢的瞬間哄辣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工赠尾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留力穗,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓气嫁,卻偏偏與公主長得像当窗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子寸宵,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • 1 背景 不能只分析源碼呀崖面,分析的同時也要整理歸納基礎知識,剛好有人微博私信讓全面說說Android的動畫梯影,所以今...
    未聞椛洺閱讀 2,691評論 0 10
  • 轉載一篇高質量博文巫员,原地址請戳這里轉載下來方便今后查看。1 背景不能只分析源碼呀甲棍,分析的同時也要整理歸納基礎知識简识,...
    Elder閱讀 1,937評論 0 24
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜感猛,今天將帶大家一窺ios動畫全貌七扰。在這里你可以看...
    每天刷兩次牙閱讀 8,465評論 6 30
  • 一、現代前端開發(fā) 1.1 ES6 —— 新一代的JavaScript標準 1.1.1 語法特性 const陪白、let...
    殺破狼real閱讀 1,822評論 0 3