轉載請註明出處(萬分感謝!):
http://blog.csdn.net/javazejian/article/details/52273733
出自【zejian的博客】
關聯文章:
走進絢爛多彩的屬性動畫-Property Animation(上篇)
走進絢爛多彩的屬性動畫-Property Animation之Interpolator和TypeEvaluator(下篇)
屬性動畫-Property Animation之ViewPropertyAnimator 你應該知道的一切
Android佈局動畫之animateLayoutChanges與LayoutTransition
1.屬性動畫概述
動畫一直是App增強用戶交互和用戶體驗的一個重要環節,特別是在某些提示場景或者廣告場景中,合理使用動畫可以給用戶帶來更加愉悅的使用體驗,因此我們很有必要掌握動畫的使用及其原理,從本篇開始,我們就來全面深入瞭解屬性動畫的使用及其原理。我們知道在早期的Android版本中,由於動畫機制不健全,如補間動畫只能作用於View對象上而且補間動畫並不能響應View的點事件,Android 3.0以後google官方推出了新的動畫框架即屬性動畫,在填補了補間動畫的缺點後還要擁有更加靈活的動畫操作,屬性動畫不僅能作用在View上而且還作用在非View的對象上,可以說是任意對象,只要我們想添加動畫效果,屬性動畫都可以滿足我們的要求,而且從名稱我們就可以看出,屬性,屬性,顧名思義就是作用在對象的屬性上,因此可以從根本上解決補間動畫無法響應View對象點擊事件的問題。當然Android 3.0以下是使用不了屬性動畫的,不過我們可以使用nineoldandroids開源動畫庫來兼容低版本,ninooldandroidsx下載網址:http://nineoldandroids.com
2.原理簡要概述
在開始之前,我們先來簡單瞭解一下屬性動畫的工作原理,以便我們後續更好的學習。Android屬性動畫要求作用的對象提供該屬性的set方法和get方法,這是因爲屬性動畫內部會根據我們傳遞的屬性初始值和最終值,然後多次去調用set方法(內部自動調用),每次傳遞給set方法的值都是變化的,也就是說隨着時間的推移,所傳遞的值越來越接近最終值,在這個過程中也就形成了動畫效果。而當開始動畫時沒有我們沒有提供初始值,這時動畫內部就回去調用get方法獲取,因此如果我們沒有傳遞初始值就必須確保作用的對象擁有get方法,否則動畫內部調用時有可能因爲沒有get方法而造成程序crash。我們通過下圖協助我們理解這個過程:
很顯然View的動畫在40ms通過不斷設置setX()的值來改變View的屬性X的,從而通過這種平滑的過度達到動畫的效果。實際上動畫內部是通過反射實現對View的屬性賦值操作。好~,到這裏我們大概地瞭解完屬性動畫的工作原理即可,不太明白也沒關係,我們接下來的分析再慢慢領會,下面開始分析屬性動畫的常規用法。
3.屬性動畫的常規用法
3.1代碼中的使用
3.1.1 ObjectAnimator
屬性動畫主要包含ObjectAnimator與ValueAnimator以及AnimatorSet 三個常用動畫類。我們先來了解一下ObjectAnimator,ObjectAnimator繼承自ValueAnimator,是一個操作對象的屬性的動畫類,可以說是一個細分類,我們創建一個ObjectAnimator只需要通過其靜態工廠類即可,基本代碼如下:
//函數原型,針對屬性float值類型的變化
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values)
//使用代碼
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(view,"x",10f,100f);
//函數原型,針對屬性int值類型的變化
public static ObjectAnimator ofInt(Object target, String propertyName, int... values)
//使用代碼
ObjectAnimator objectAnimator=ObjectAnimator.ofInt(view,"x",10,100);
//函數原型,針對屬性顏色的變化
public static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
//使用代碼
ObjectAnimator objectAnimator=ObjectAnimator.ofArgb(view,"backgroundColor",0xffffff80,0xffff80fe);
以上三種創建方式的參數都包含一個作用的對象和對象的屬性名稱以及一個可變參數,我們要注意的的是這個屬性必須要有get和set函數(在我們不確定是否set和get方法時,不妨查看一下view的源碼),因爲動畫內部會通過java反射機制來調用set函數修改對象屬性值。接着我們來看一個簡單的案例:
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(btn,"translationX",0f,200f);
objectAnimator.setDuration(3000);
`objectAnimator.start(); `
效果如下:
通過ObjectAnimator的靜態工廠方法,創建一個ObjectAnimator對象,我們傳遞的第一個參數就是要動畫要作用的view對象btn,第二個參數則是要操作的屬性,而後面的一個參數是一個可變參數數組,當傳遞參數數組只有一個數值時,動畫內部去調用get方法獲取初始化參數,並把傳遞參數作爲目標值,而當傳遞參數的數組中有兩個數值時,則會把第一個數值作爲初始值,這時就不會去調用get了,只會調用set方式設置動畫過程,並以第二個數值作爲目標值,如我們上面傳遞了0和200,其中0就是初始值,而200就是目標值。其後的設置,setDuration(3000);
設置動畫時間,objectAnimator.start();
則是啓動動畫。
到這裏,我們可能會有個疑問,那如果數組中值大於2個呢?實際上如果多於2個數值,則繼續執行動畫,如下我們改爲3個數值,讓button回到原點,
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(btn,"translationX",0f,200f,0f);
objectAnimator.setDuration(3000);
objectAnimator.start();
效果如下:
這裏再次強調一次,在使用ObjectAnimator的時候,一定要確保操作的屬性必須具有get,set方法,不然輕則不起效果,重則直接crash。
下面我們再給出一個顏色漸變的動畫效果的案例我們改變一個對象的背景色屬性,讓背景在3秒內實現顏色漸變效果
package com.zejian.animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.btn);
ObjectAnimator colorAima=ObjectAnimator.ofArgb(btn,"backgroundColor",
getResourcesColor(R.color.colorAccent),
getResourcesColor(R.color.colorPrimary));
//設置無限循環
colorAima.setRepeatCount(ValueAnimator.INFINITE);
//設置反轉效果
colorAima.setRepeatMode(ValueAnimator.REVERSE);
colorAima.setDuration(3000);
colorAima.start();
}
/**
* 獲取資源中的顏色
* @param color
* @return
*/
public int getResourcesColor(int color) {
int ret = 0x00ffffff;
try {
ret =getResources().getColor(color);
} catch (Exception e) {
}
return ret;
}
}
代碼比較簡單,我們直接看效果:
效果很明顯,是不是發現屬性動畫的代碼特別簡潔,我們再來看看屬性動畫如何實現補間動畫的效果,先來一個alpha動畫,代碼如下:
ObjectAnimator.ofFloat(btn,"alpha",0,1).setDuration(5000).start();
一句代碼搞定,效果如下:
乾脆來個旋轉,平移,透明變化,縮放一起播放的動畫:
AnimatorSet set = new AnimatorSet();
set.playTogether( ObjectAnimator.ofFloat(btn,"alpha",0,1,0.5f,1),
ObjectAnimator.ofFloat(btn,"rotation",0,360,0),
ObjectAnimator.ofFloat(btn,"scaleX",0,1,1.5f,1),
ObjectAnimator.ofFloat(btn,"scaleY",0,1,1.5f,1),
ObjectAnimator.ofFloat(btn,"translationX",0,125),
ObjectAnimator.ofFloat(btn,"translationY",0,125)
);
set.setDuration(5000).start();
其中AnimatorSet爲集合動畫(後面會分析),效果如下:
到此我們對ObjectAnimator對象的使用基本上有了比較清晰的瞭解了。最後這裏給出旋轉,平移,透明變化,縮放幾個屬性值的小結:
屬性 | 含義 |
---|---|
translationX與translationY | 這兩個屬性作爲一種增量來控制View對象相對於它父容器的左上角座標偏移的位置 |
scaleX與scaleY | 控制View對象圍繞它的支點進行2D縮放 |
alpha | 表示View對象的alpha透明度。默認值1,不透明,0代表完全透明,即不可見。 |
rotation、rotationX、rotationY | 這三個屬性控制View對象圍繞支點進行旋轉, 其中rotation對應2D,rotationX、rotationY對應3D旋轉 |
pivotX、pivotY | 兩個屬性控制着View對象的支點位置,包含旋轉和縮放都圍繞這個支點變換和處理。 默認值爲View對象的中心,如view.setPivotX(0.5f);view.setPivotY(0.5f); |
至於ObjectAnimator對象的一些常用方法,我們後面會和ValueAnimator對象放在一起小結。
3.1.2 ValueAnimator
接着我們繼續瞭解另一個動畫類ValueAnimator,它是整個屬性動畫機制當中的核心類,前面我們已經提到了,屬性動畫的運行機制是通過內部不斷地調用set方式對屬性的值進行操作來實現動畫的,而初始值和結束值之間的動畫過渡就是由ValueAnimator這個類來負責計算的。說明白點就是ValueAnimator本身不作用於任何對象,它作用的是一個數值,ValueAnimator本身沒有任何動畫效果,我們只需要將初始值和結束值提供給ValueAnimator,並且告訴它動畫運行的時長,ValueAnimator類就會自動幫我們完成從初始值平滑地過渡到結束值的計算。我們來看一個例子:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(500);
anim.start();
從代碼中看,我們並沒有傳入任何對象,只是傳遞了0-1的數值,這時我們開啓動畫,ValueAnimator便會在500內對[0,1]數值做運動。我們不妨給ValueAnimator添加一個監聽器AnimatorUpdateListener(這個接口後面會分析),然後輸出計算過程中的估值數,代碼修改如下:
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(500);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//獲取當前動畫的進度值
float currentValue = (float) animation.getAnimatedValue();
Log.e("wzj","fraction-->"+currentValue);
}
});
anim.start();
打印LOG如下:
從log可以看出ValueAnimator類確實是對[0,1]的數值做了“運動”。實際上從上面的代碼我們也可以看出,如果我們想通過ValueAnimator實現view對象動畫效果,也是沒問題的,因爲我們可以在監聽器中利用當前的數據值,來修改我們view對象的屬性值,這樣也就是我們自己定義動畫了,最終也實現了動畫效果的。我們這裏利用ValueAnimator來實現一個前面的類似的縮放+透明變化案例:
ValueAnimator anim = ValueAnimator.ofFloat(0f,1f);
anim.setDuration(5000);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//設置當前動畫的進度值
btn.setAlpha((Float) animation.getAnimatedValue());
btn.setScaleX((Float) animation.getAnimatedValue());
btn.setScaleY((Float) animation.getAnimatedValue());
}
});
anim.start();
我們設置了[0,1]的變化範圍,然後在ValueAnimator.AnimatorUpdateListener監聽器(該類監聽動畫執行的每一幀)中給btn設置了縮放動畫和透明變化的動畫,效果如下:
除了ofFloat方法外還有ofInt方法,ofObject方法,如下:
ValueAnimator.ofInt(0,100).setDuration(500).start();
這個比較簡單,不過多分析了,還有另外一個方法
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)
它有兩個參數,第一個是自定義的Evaluator,第二個是可變長參數,Object類型的; Evaluator是一個類型估值器(後面我們會分析它)。對於ofObject這個方法我們打算後面分析完Evaluator再來詳細分析,所以這裏就不深入先哈~。除了以上所說的,ValueAnimator還可以設置動畫的播放次數、播放模式。如下:
//設置模式ValueAnimator.REVERSE表示反向播放,ValueAnimator.RESTART表示從頭播放
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
//設置次數,ValueAnimator.INFINITE表示無限循環
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
註釋很明白,不過多解釋了。這樣我們對ValueAnimator也有了比較清楚的瞭解了,最後總結一下ValueAnimator和ObjectAnimator的共同常用方法(未涉及的請查閱官方API,哈~):
方法名稱 | 說明 |
---|---|
ValueAnimator ofInt(int… values) | 可變參數傳入int值,計算所給時間內的變動值或者動畫過程 |
ValueAnimator ofFloat(float… values) | 可變參數傳入float值,計算所給時間內的變動值或者動畫過程 |
ValueAnimator ofObject(TypeEvaluator evaluator, Object… values) | 它有兩個參數,第一個是自定義的Evaluator,是一個類型估值器,第二個是可變長參數, 設置所傳遞對象的屬性數值變化範圍,後面會詳細分析這個方法,這裏不明白沒有關係。 |
Object getAnimatedValue() | 返回的類型是一個Object原始類型,可能會強轉成int類型或者Float類型,表示當前動畫的進度值 |
ValueAnimator setDuration(long duration) | 設置動畫時長,單位是毫秒 |
void start() | 啓動動畫 |
void setRepeatCount(int value) | 設置循環次數,設置爲INFINITE表示無限循環 |
void setRepeatMode(int value) | 設置循環模式,ValueAnimator.REVERSE表示反向播放,ValueAnimator.RESTART表示從頭播放 |
void cancel() | 取消動畫 |
setStartDelay(long startDelay) | 就是設置多久後動畫纔開始,單位毫秒 |
addUpdateListener(AnimatorUpdateListener listener) | 添加動畫監聽器,監聽動畫的每一幀,也就是每一幀都會回調該監聽器的方法 |
addListener(AnimatorListener listener) | 添加動畫監聽器,監聽動畫的開始,結束,取消以及重複播放事件, 一般使用AnimatorListenerAdapter實現。 |
3.1.3 組合動畫
3.1.3.1 PropertyValueHolder
在屬性動畫中,我們可能會同一個View對象的多個屬性同時作用多種動畫,這時我們就可以是用PropertyValueHolder來實現。下面我們直接看一個案例:
PropertyValuesHolder p1=PropertyValuesHolder.ofFloat("scaleX",0.5f,1);
PropertyValuesHolder p2=PropertyValuesHolder.ofFloat("scaleY",0.5f,1);
PropertyValuesHolder p3=PropertyValuesHolder.ofFloat("translationX",0,200f);
ObjectAnimator.ofPropertyValuesHolder(btn,p1,p2,p3).setDuration(500).start();
我們在代碼中,使用了PropertyValuesHolder對象來控制scaleX,scaleY,translationX三個屬性,最後設置到ObjectAnimator中,開啓動畫,3個動畫便同時開始了。好~,對PropertyValuesHolder我們暫時瞭解這麼就行,下面看AnimatorSet。
3.1.3.2 AnimatorSet
對於一個View對象的多個屬性同時作用多個對象時,在上面我們已經使用PropertyValuesHolder實現了這種效果,而AnimatorSet也可以實現相同的效果,同時AnimatorSet還能實現更爲精確的動畫順序控制。我們利用AnimatorSet實現上面的PropertyValuesHolder的動畫效果,代碼如下:
ObjectAnimator b1 = ObjectAnimator.ofFloat(btn,"scaleX",0.5f,1);
ObjectAnimator b2 = ObjectAnimator.ofFloat(btn,"scaleY",0.5f,1);
ObjectAnimator b3 = ObjectAnimator.ofFloat(btn,"translationX",0f,200f);
AnimatorSet animatorSet =new AnimatorSet();
animatorSet.setDuration(500);
animatorSet.playTogether(b1,b2,b3);
animatorSet.start();
其中playTogether()方法就是將3個動畫一起同時執行,還有playSequentially()方法則是3個動畫按順序執行。這裏我們來看看AnimatorSet的一些常用的控制動畫順序的方法
方法名稱 | 方法說明 |
---|---|
playTogether(Animator… items) | items表示每個子動畫,該方法表示同時執行集合動畫中的所有動畫。 |
playSequentially(Animator… items) | items表示每個子動畫,該方法表示按順序執行集合動畫中的所有動畫。 |
play(Animator anim) | 向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator) 將會返回一個AnimatorSet.Builder的實例,並會執行這個動畫 |
after(Animator anim) | 將現有動畫插入到傳入的動畫之後執行 |
after(long delay) | 將現有動畫延遲指定毫秒後執行 |
before(Animator anim) | 將現有動畫插入到傳入的動畫之前執行 |
with(Animator anim) | 將現有動畫和傳入的動畫同時執行 |
瞭解完這幾個主要方法後,我們看一個例子,代碼如下:
ObjectAnimator moveIn = ObjectAnimator.ofFloat(btn, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(btn, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(btn, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();
根據前面的方法說明,我們可猜到最先執行的動畫應該是moveIn,然後rotate和fadeInOut再同時執行,下面我們看看最終效果:
很顯然,跟我們猜測是一樣的。好~,對於AnimatorSet我們就先了解到這裏,哈~。
3.2 xml中的使用
上面介紹了屬性動畫的代碼實現方式,這裏我們來簡單介紹一下屬性動畫在xml中的實現,文件需要存放在res/animator/文件夾下,沒有的自建animator文件夾即可。其語法如下:
<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>
其中AnimatorSet對應<set>標籤,ObjectAnimator對應<objectAnimator>標籤,ValueAnimator則對應<animator>標籤,從語法可以看出<set>標籤的android:ordering屬性有兩種取值,分別爲together和sequentially,together表示動畫集合中的子動畫同時播放,together爲默認值,而sequentially則表示子動畫順序播放。接着我們來繼續說明一下<animator>標籤和<objectAnimator>標籤的屬性及其的含義。
屬性 | 屬性值及其含義 |
---|---|
android:propertyName | 屬性名稱,例如一個view對象的”alpha”和”backgroundColor”。 |
android:valueFrom | 變化開始值 |
android:valueTo | 變化結束值 |
android:valueType | 變化值類型即android:propertyName所指定屬性的類型,有intType和floatType可選, 如果android:propertyName所指定屬性表示顏色的話,則不需要指定android:valueType的值, 系統會自動對顏色類型的屬性進行處理 |
android:duration | 動畫持續時間 |
android:startOffset | 動畫開始延遲的時間,也就是動畫多久後纔開始播放 |
android:repeatCount | 重複次數,-1表示無限重複 |
android:repeatMode | 重複模式,前提是android:repeatCount有值,它有兩種值:”reverse”和”repeat”, 第一個表示反向重複,第二個爲順序重複。 |
<animator>標籤和<objectAnimator>標籤相比,<animator>標籤只少了一個 android:propertyName屬性,其他基本一樣。下面我們簡單來實現一個案例:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together"
>
<objectAnimator
android:propertyName="scaleX"
android:duration="5000"
android:valueFrom="0.5"
android:valueTo="1"
android:valueType="floatType"
/>
<objectAnimator
android:propertyName="scaleY"
android:duration="5000"
android:valueFrom="0.5"
android:valueTo="1"
android:valueType="floatType"
/>
<objectAnimator android:duration="5000"
android:propertyName="alpha"
android:valueFrom="0.5"
android:valueTo="1"
android:valueType="floatType" />
<objectAnimator android:duration="5000"
android:propertyName="backgroundColor"
android:valueFrom="@color/green"
android:valueTo="@color/colorAccent" />
</set>
我們讓一個button按鈕的ScaleX和ScaleY屬性從0.5變化到1,透明屬性alpha也是從0.5變化到1,接着其背景顏色我們從綠色漸變到粉紅色,三個動畫效果同時執行,代碼中添加該動畫屬性如下:
AnimatorSet set= (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.btn_anim);
set.setTarget(btn);
set.start()
效果如下:
到此屬性動畫的xml使用方式就介紹完了,但在實際開發中還是比較建議使用代碼來實現屬性動畫,因爲代碼實現比xml實現簡單,同時有時候我們根本無法知道一個屬性的初始化值或者結束值,因爲android手機千姿百態,屏幕適配各不相同,我們必須採用動態計算的方式來獲取屬性的值,才能最終確定要執行屬性動畫的控件的屬性值。
4. 屬性動畫的監聽器
關於屬性動畫的監聽器我們前面也瞭解了不少,這裏我們來重新認識下它們。屬性動畫提供了兩個用於監聽動畫播放過程的監聽器,它們是AnimatorUpdateListener和AnimatorListener(通用的動畫監聽器)。其中AnimatorUpdateListener定義如下:
public static interface AnimatorUpdateListener {
/**
* <p>Notifies the occurrence of another frame of the animation.</p>
*
* @param animation The animation which was repeated.
*/
void onAnimationUpdate(ValueAnimator animation);
}
AnimatorUpdateListener只有一個onAnimationUpdate的方法,這個監聽器會監聽動畫的整個過程,由於動畫是由許多幀組成的,沒播放一幀,onAnimationUpdate方法就會被回調一次,因此我們可以利用這個特性實現一些特殊的效果。
AnimatorListener定義如下:
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
從代碼看出AnimatorListener可以監聽動畫的開始,結束,取消,重複播放。谷歌官方考慮到有時我們並不想同時實現這4是個方法,於是官方提供了更好的解決方案,AnmitorListenerAdapter類,它是AnimatorListener的適配器,這樣的話我們也就不用同時實現4個方法了,而是可以有選擇地實現我們需要的方法。案例就不寫了,比較簡單,前面也有涉及到,好~,到此監聽器也說完了,感覺這篇已經寫了挺長了,好吧,到此先告一段落,剩餘下篇再聊,主要是時間插值器(TimeInterpolator)和類型估算器(TypeEvaluator)。
走進絢爛多彩的屬性動畫-Property Animation(上篇)
走進絢爛多彩的屬性動畫-Property Animation之Interpolator和TypeEvaluator(下篇)
屬性動畫-Property Animation之ViewPropertyAnimator 你應該知道的一切
Android佈局動畫之animateLayoutChanges與LayoutTransition
主要參考資料
http://developers.android.com
http://blog.csdn.net/guolin_blog/article/details/43536355
《android開發藝術探索》