1. 程式人生 > >android.animation(5) - PropertyValuesHolder與Keyframe(轉)

android.animation(5) - PropertyValuesHolder與Keyframe(轉)

狀態 一個 通過 返回值 成對 1.0 內部實現 分代 tail

前幾篇給大家講了ValueAnimator、ObjectAnimator的知識,講解了它們ofInt(),ofFloat(),ofObject()函數的用法。細心的同學可能會註意到,ValueAnimator、ObjectAnimator除了這些創建Animator實例的方法以外,都還有一個方法:

/** 
 * valueAnimator的 
 */  
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values)   
/** 
 * ObjectAnimator的 
 
*/ public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)

也就是說ValueAnimator和ObjectAnimator除了通過ofInt(),ofFloat(),ofObject()創建實例外,還都有一個ofPropertyValuesHolder()方法來創建實例,這篇文章我就帶大家來看看如何通過ofPropertyValuesHolder()來創建實例的。
由於ValueAnimator和ObjectAnimator都具有ofPropertyValuesHolder()函數,使用方法也差不多,相比而言,ValueAnimator的使用機會不多,這裏我們就只講ObjectAnimator中ofPropertyValuesHolder()的用法。相信大家懂了這篇以後,再去看ValueAnimator的ofPropertyValuesHolder(),也應該是會用的。


在這篇文章的最後,我們通過本篇內容做了一個電話響鈴的效果,效果圖如下:

技術分享圖片

一、PropertyValuesHolder

1、概述

PropertyValuesHolder這個類的意義就是,它其中保存了動畫過程中所需要操作的屬性和對應的值。我們通過ofFloat(Object target, String propertyName, float… values)構造的動畫,ofFloat()的內部實現其實就是將傳進來的參數封裝成PropertyValuesHolder實例來保存動畫狀態。在封裝成PropertyValuesHolder實例以後,後期的各種操作也是以PropertyValuesHolder為主的。


說到這裏,大家就知道這個PropertyValuesHolder是有多有用了吧,上面我們也說了,ObjectAnimator給我們提供了一個口子,讓我們自己構造PropertyValuesHolder來構造動畫。

public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values) 

PropertyValuesHolder中有很多函數,有些函數的api等級是11,有些函數的api等級是14和21;
高api的函數我們就不講了,只講講api 11的函數的用法。有關各個函數的api等級,大家可以參考《Google文檔:PropertyValuesHolder》
首先,我們來看看創建實例的函數:

public static PropertyValuesHolder ofFloat(String propertyName, float... values)  
public static PropertyValuesHolder ofInt(String propertyName, int... values)   
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,Object... values)  
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) 

這裏總共有四個創建實例的方法,這一段我們著重講ofFloat、ofInt和ofObject的用法,ofKeyframe我們單獨講。

2、PropertyValuesHolder之ofFloat()、ofInt()

(1)ofFloat()、ofInt()

我們先來看看它們的構造函數:

public static PropertyValuesHolder ofFloat(String propertyName, float... values)  
public static PropertyValuesHolder ofInt(String propertyName, int... values) 

其中:

  • propertyName:表示ObjectAnimator需要操作的屬性名。即ObjectAnimator需要通過反射查找對應屬性的setProperty()函數的那個property.
  • values:屬性所對應的參數,同樣是可變長參數,可以指定多個,還記得我們在ObjectAnimator中講過,如果只指定了一個,那麽ObjectAnimator會通過查找getProperty()方法來獲得初始值。不理解的同學請參看《Animation動畫詳解(七)——ObjectAnimator基本使用》

大家看這些參數是不是很眼熟,讓我們看一下ObjectAnimator的ofFloat是怎麽樣的:

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values);

看到沒,在ObjectAnimator.ofFloat中只比PropertyValuesHolder的ofFloat多了一個target,其它都是完全一樣的!
好了,我們在講完PropertyValuesHolder的ofFloat函數以後,我們再來看看如何將構造的PropertyValuesHolder實例設置進ObjectAnimator吧。

(2)、ObjectAnimator.ofPropertyValuesHolder()

在開篇時,我們也講了ObjectAnimator給我們提供了一個設置PropertyValuesHolder實例的入口:

public static ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)   

其中:

  • target:指需要執行動畫的控件
  • values:是一個可變長參數,可以傳進去多個PropertyValuesHolder實例,由於每個PropertyValuesHolder實例都會針對一個屬性做動畫,所以如果傳進去多個PropertyValuesHolder實例,將會對控件的多個屬性同時做動畫操作。

(3)、示例

下面我們就舉個例子來如何通過PropertyValuesHolder的ofFloat、ofInt來做動畫的。
效果圖如下:

技術分享圖片

這個動畫很簡單,就是在點擊按鈕的時候,給textView做動畫,框架代碼就不再講了,我們主要來看看操作textview動畫的代碼。
動畫代碼為:

PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f);  
PropertyValuesHolder colorHolder = PropertyValuesHolder.ofInt("BackgroundColor", 0xffffffff, 0xffff00ff, 0xffffff00, 0xffffffff);  
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, colorHolder);  
animator.setDuration(3000);  
animator.setInterpolator(new AccelerateInterpolator());  
animator.start();

在這裏,我們創建了兩個PropertyValuesHolder實例,第一個rotationHolder:

PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f);  

使用ofFloat函數創建,屬性值是Rotation,對應的是View類中SetRotation(float rotation)函數。後面傳進去很多值,讓其左右擺動。
第二是動畫是改變背景色的colorHolder

PropertyValuesHolder colorHolder = PropertyValuesHolder.ofInt("BackgroundColor", 0xffffffff, 0xffff00ff, 0xffffff00, 0xffffffff);  

這裏使用的是ofInt函數創建的,它操作的屬性是BackgroundColor,對應的是View類中的setBackgroundColor(int color)函數,後面傳進去的16進制顏色值讓其在這些顏色值間變化。有關顏色值的變化,大家可以參考《Animation動畫詳解(七)——ObjectAnimator基本使用》中第三部分《常用函數》
最後通過ObjectAnimator.ofPropertyValuesHolder將rotationHolder、colorHolder設置給mTextView,構造出ObjectAnimator對象。然後開始動畫即可

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, colorHolder);  
animator.setDuration(3000);  
animator.setInterpolator(new AccelerateInterpolator());  
animator.start();

好了,到這裏有關PropertyValuesHolder的ofInt和ofFloat函數的用法就講完了,大家可以看到PropertyValuesHolder使用起來也很容易,下面我們再來看看PropertyValuesHolder的ofObject的使用方法。

3、PropertyValuesHolder之ofObject()

(1)、概述

我們先來看一下ofObject的構造函數

public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator,Object... values)  
  • propertyName:ObjectAnimator動畫操作的屬性名;
  • evaluator:Evaluator實例,Evaluator是將當前動畫進度計算出當前值的類,可以使用系統自帶的IntEvaluator、FloatEvaluator也可以自定義,有關Evaluator的知識,大家可以參考《Animation動畫詳解(五)——ValueAnimator高級進階(一)》
  • values可變長參數,表示操作動畫屬性的值

它的各個參數與ObjectAnimator.ofObject的類似,只是少了target參數而已

public static ObjectAnimator ofObject(Object target, String propertyName,TypeEvaluator evaluator, Object... values)  

(2)、示例

下面我們就講講PropertyValuesHolder.ofObject()函數的用法
本示例的效果圖如下

技術分享圖片

這裏實現的效果與《Animation動畫詳解(六)——ValueAnimator高級進階(二)》實現的效果相同,即通過自字義的CharEvaluator來自動實現字母的改變與計算。
首先是自定義一個CharEvaluator,通過進度值來自動計算出當前的字母:

public class CharEvaluator implements TypeEvaluator<Character> {  
    @Override  
    public Character evaluate(float fraction, Character startValue, Character endValue) {  
        int startInt  = (int)startValue;  
        int endInt = (int)endValue;  
        int curInt = (int)(startInt + fraction *(endInt - startInt));  
        char result = (char)curInt;  
        return result;  
    }  
} 

有關數字與字符間轉換的原理已經在《Animation動畫詳解(六)——ValueAnimator高級進階(二)》講述,就不再細講。這個CharEvaluator也是直接從這篇文章中拿過來的,強烈建議大家對這個系列文章從頭開始看。
從CharEvaluator中可以看出,從CharEvaluator中產出的動畫中間值類型為Character類型。TextView中雖然有setText(CharSequence text) 函數,但這個函數的參數類型是CharSequence,而不是Character類型。所以我們要自定義一個類派生自TextView來改變TextView的字符

public class MyTextView extends TextView {  
    public MyTextView(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
    public void setCharText(Character character){  
        setText(String.valueOf(character));  
    }  
} 

在這裏,我們定義了一個方法setCharText(Character character),參數就是Character類型,我們知道這個方法所對應的屬性是CharText;
最後MyActivity,在點擊按鈕的時候開始動畫,核心代碼為:

public class MyActivity extends Activity {  
    private Button btn;  
    private TextView mTextView;  
    private MyTextView mMyTv;  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
  
        mMyTv = (MyTextView)findViewById(R.id.mytv);  
        btn = (Button) findViewById(R.id.btn);  
        btn.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                doOfObjectAnim();  
            }  
        });  
    }  
  
    private void doOfObjectAnim(){  
        PropertyValuesHolder charHolder = PropertyValuesHolder.ofObject("CharText",new CharEvaluator(),new Character(‘A‘),new Character(‘Z‘));  
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mMyTv, charHolder);  
        animator.setDuration(3000);  
        animator.setInterpolator(new AccelerateInterpolator());  
        animator.start();  
    }  
}  

這部分代碼,很好理解,在點擊按鈕的時候執行doOfObjectAnim()方法:

PropertyValuesHolder charHolder = PropertyValuesHolder.ofObject("CharText",new CharEvaluator(),new Character(‘A‘),new Character(‘Z‘));  
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mMyTv, charHolder);  
animator.setDuration(3000);  
animator.setInterpolator(new AccelerateInterpolator());  
animator.start();  

首先是根據PropertyValuesHolder.ofObject生成一個PropertyValuesHolder實例,註意它的屬性就是CharText,所對應的set函數就是setCharText,由於CharEvaluator的中間值是Character類型,所以CharText屬性所對應的完整的函數聲明為setCharText(Character character);這也就是我們為什麽要自定義一個MyTextView原因,就是因為TextView中沒有setText(Character character)這樣的函數。
然後就是利用ObjectAnimator.ofPropertyValuesHolder生成ObjectAnimator實例了,最後就是對animator設置並start了,沒什麽難度,就不再講了。

下面就開始最重要的部分了,有關KeyFrame的知識。

二、Keyframe

1、概述

通過前面幾篇的講解,我們知道如果要控制動畫速率的變化,我們可以通過自定義插值器,也可以通過自定義Evaluator來實現。但如果真的讓我們為了速率變化效果而自定義插值器或者Evaluator的話,恐怕大部分同學會有一萬頭草泥馬在眼前奔過,因為大部分的同學的數學知識已經還給老師了。
為了解決方便的控制動畫速率的問題,谷歌為了我等屁民定義了一個KeyFrame的類,KeyFrame直譯過來就是關鍵幀。
關鍵幀這個概念是從動畫裏學來的,我們知道視頻裏,一秒要播放24幀圖片,對於制作flash動畫的同學來講,是不是每一幀都要畫出來呢?當然不是了,如果每一幀都畫出來,那估計做出來一個動畫片都得要一年時間;比如我們要讓一個球在30秒時間內,從(0,0)點運動到(300,200)點,那flash是怎麽來做的呢,在flash中,我們只需要定義兩個關鍵幀,在動畫開始時定義一個,把球的位置放在(0,0)點;在30秒後,再定義一個關鍵幀,把球的位置放在(300,200)點。在動畫 開始時,球初始在是(0,0)點,30秒時間內就adobe flash就會自動填充,把球平滑移動到第二個關鍵幀的位置(300,200)點;
通過上面分析flash動畫的制作原理,我們知道,一個關鍵幀必須包含兩個原素,第一時間點,第二位置。即這個關鍵幀是表示的是某個物體在哪個時間點應該在哪個位置上。
所以谷歌的KeyFrame也不例外,KeyFrame的生成方式為:

Keyframe kf0 = Keyframe.ofFloat(0, 0);  
Keyframe kf1 = Keyframe.ofFloat(0.1f, -20f);  
Keyframe kf2 = Keyframe.ofFloat(1f, 0);

上面生成了三個KeyFrame對象,其中KeyFrame的ofFloat函數的聲明為:

public static Keyframe ofFloat(float fraction, float value)  
  • fraction表示當前的顯示進度,即從加速器中getInterpolation()函數的返回值;
  • value表示當前應該在的位置

比如Keyframe.ofFloat(0, 0)表示動畫進度為0時,動畫所在的數值位置為0;Keyframe.ofFloat(0.25f, -20f)表示動畫進度為25%時,動畫所在的數值位置為-20;Keyframe.ofFloat(1f,0)表示動畫結束時,動畫所在的數值位置為0;
在理解了KeyFrame.ofFloat()的參數以後,我們來看看PropertyValuesHolder是如何使用KeyFrame對象的:

public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values) 
  • propertyName動畫所要操作的屬性名
  • valuesKeyframe的列表,PropertyValuesHolder會根據每個Keyframe的設定,定時將指定的值輸出給動畫。

所以完整的KeyFrame的使用代碼應該是這樣的:

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
Keyframe frame2 = Keyframe.ofFloat(1, 0);  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2);  
 Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder);  
animator.setDuration(1000);  
animator.start();  

第一步:生成Keyframe對象;
第二步:利用PropertyValuesHolder.ofKeyframe()生成PropertyValuesHolder對象
第三步:ObjectAnimator.ofPropertyValuesHolder()生成對應的Animator

2、示例

在了解了Keyframe如何使用以後,下面我們就來用一個例子來看看Keyframe的使用方法。
本例的效果圖如下:

技術分享圖片

看起來跟開篇的一樣,仔細對比一下,還是有不同的,這裏只是實現了左右震動,但並沒有放大效果。

(1)、main.xml

我們先來看看布局代碼,代碼很簡單,一個btn,一個imageview

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
              android:orientation="vertical"  
              android:layout_width="fill_parent"  
              android:layout_height="fill_parent">  
    <Button  
            android:id="@+id/btn"  
            android:layout_width="wrap_content"  
            android:layout_height="wrap_content"  
            android:text="start anim"/>  
  
    <ImageView  
            android:id="@+id/img"  
            android:layout_width="150dp"  
            android:layout_height="wrap_content"  
            android:scaleType="fitCenter"  
            android:layout_gravity="center_horizontal"  
            android:src="@drawable/phone"/>  
</LinearLayout>  

這段布局代碼沒什麽難度,就不再講了,下面來看看MyActivity中的處理

(2)、MyActivity.java

public class MyActivity extends Activity {  
    private ImageView mImage;  
    private Button mBtn;  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        mImage = (ImageView)findViewById(R.id.img);  
        mBtn = (Button)findViewById(R.id.btn);  
        mBtn.setOnClickListener(new View.OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                doOfFloatAnim();  
            }  
        });  
    }  
  
    private void doOfFloatAnim(){  
        Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
        Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
        Keyframe frame2 = Keyframe.ofFloat(0.2f, 20f);  
        Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  
        Keyframe frame4 = Keyframe.ofFloat(0.4f, 20f);  
        Keyframe frame5 = Keyframe.ofFloat(0.5f, -20f);  
        Keyframe frame6 = Keyframe.ofFloat(0.6f, 20f);  
        Keyframe frame7 = Keyframe.ofFloat(0.7f, -20f);  
        Keyframe frame8 = Keyframe.ofFloat(0.8f, 20f);  
        Keyframe frame9 = Keyframe.ofFloat(0.9f, -20f);  
        Keyframe frame10 = Keyframe.ofFloat(1, 0);  
        PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2,frame3,frame4,frame5,frame6,frame7,frame8,frame9,frame10);  
  
        Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder);  
        animator.setDuration(1000);  
        animator.start();  
    }  
}  

這段代碼難度也不大,在點擊按鈕的時候,執行doOfFloatAnim()函數,關鍵問題在doOfFloatAnim()上:
首先,我們定義了11個keyframe:

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
Keyframe frame2 = Keyframe.ofFloat(0.2f, 20f);  
Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  
Keyframe frame4 = Keyframe.ofFloat(0.4f, 20f);  
Keyframe frame5 = Keyframe.ofFloat(0.5f, -20f);  
Keyframe frame6 = Keyframe.ofFloat(0.6f, 20f);  
Keyframe frame7 = Keyframe.ofFloat(0.7f, -20f);  
Keyframe frame8 = Keyframe.ofFloat(0.8f, 20f);  
Keyframe frame9 = Keyframe.ofFloat(0.9f, -20f);  
Keyframe frame10 = Keyframe.ofFloat(1, 0);  

在這些keyframe中,首先指定在開始和結束時,旋轉角度為0,即恢復原樣:

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame10 = Keyframe.ofFloat(1, 0);  

然後在過程中,讓它左右旋轉,比如在進度為0.2時,旋轉到左邊20度位置:

Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  

然後在進度為0.3時,旋轉到右邊20度位置:

Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  

其它類似。正是因為來回左右的旋轉,所以我們看起來就表現為在震動
然後,根據這些Keyframe生成PropertyValuesHolder對象,指定操作的屬性為rotation

PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2,frame3,frame4,frame5,frame6,frame7,frame8,frame9,frame10);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder);  
animator.setDuration(1000);  
animator.start();  

最後,利用ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder)生成ObjectAnimator對象,並開始動畫。
到這裏想必大家已經對Keyframe有了初步的認識,下面我們就來詳細的講講Keyframe;

3、Keyframe之ofFloat、ofInt與常用函數

(1)、ofFloat、ofInt

上面我們看到Keyframe.ofFloat()函數的用法,其實Keyframe除了ofFloat()以外,還有ofInt()、ofObject()這些創建Keyframe實例的方法,Keyframe.ofObject()我們下部分再講,這部分,我們著重看看ofFloat與ofInt的構造函數與使用方法

/** 
 * ofFloat 
 */  
public static Keyframe ofFloat(float fraction)   
public static Keyframe ofFloat(float fraction, float value)  
/** 
 * ofInt 
 */  
public static Keyframe ofInt(float fraction)  
public static Keyframe ofInt(float fraction, int value)  

由於ofFloat和ofInt的構造函數都是一樣的,我們這裏只以ofFloat來例來說。
上面我們已經講了ofFloat(float fraction, float value)的用法,fraction表示當前關鍵幀所在的動畫進度位置,value表示當前位置所對應的值。
而另一個構造函數:

public static Keyframe ofFloat(float fraction)   

這個構造函數比較特殊,只有一個參數fraction,表示當前關鍵幀所在的動畫進度位置。那在這個進度時所對應的值要怎麽設置呢?
當然有方法啦,除了上面的構造函數,Keyframe還有一些常用函數來設置fraction,value和interpolator,定義如下:

(2)、常用函數:

/** 
 * 設置fraction參數,即Keyframe所對應的進度 
 */  
public void setFraction(float fraction)   
/** 
 * 設置當前Keyframe所對應的值 
 */  
public void setValue(Object value)  
/** 
 * 設置Keyframe動作期間所對應的插值器 
 */  
public void setInterpolator(TimeInterpolator interpolator)  

這三個函數中,插值器的作用應該是比較難理解,如果給這個Keyframe設置上插值器,那麽這個插值器就是從上一個Keyframe開始到當前設置插值器的Keyframe時,這個過程值的計算是利用這個插值器的,比如:

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
frame1.setInterpolator(new BounceInterpolator());  
Keyframe frame2 = Keyframe.ofFloat(1f, 20f);  
frame2.setInterpolator(new LinearInterpolator());  

在上面的代碼中,我們給frame1設置了插值器BounceInterpolator,那麽在frame0到frame1的中間值計算過程中,就是用的就是回彈插值器;
同樣,我們給frame2設置了線性插值器(LinearInterpolator),所以在frame1到frame2的中間值計算過程中,使用的就是線性插值器
很顯然,給Keyframe.ofFloat(0f, 0)設置插值器是無效的,因為它是第一幀

(3)、示例1——沒有插值器

下面我們就舉個例子來看下,如何使用上面的各個函數的用法,同樣是基於上面的電話響鈴的例子,如果我們只保存三幀,代碼如下:

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.5f, 100f);  
Keyframe frame2 = Keyframe.ofFloat(1);  
frame2.setValue(0f);  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder);  
animator.setDuration(3000);  
animator.start(); 

在這段代碼中,總共就只有三個關鍵幀,最後一個Keyframe的生成方法是利用:

Keyframe frame2 = Keyframe.ofFloat(1);  
frame2.setValue(0f);  

對於Keyframe而言,fraction和value這兩個參數是必須有的,所以無論用哪種方式實例化Keyframe都必須保證這兩個值必須被初始化。
這裏沒有設置插值器,會使用默認的線性插值器(LinearInterpolator)
效果圖如下:

技術分享圖片

(4)、示例2——使用插值器

下面,我們給上面的代碼加上插值器,著重看一下,插值器在哪部分起做用

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.5f, 100f);  
Keyframe frame2 = Keyframe.ofFloat(1);  
frame2.setValue(0f);  
frame2.setInterpolator(new BounceInterpolator());  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder);  
animator.setDuration(3000);  
animator.start();  

我們給最後一幀frame2添加上回彈插值器(BounceInterpolator),然後看看效果:

技術分享圖片

從效果圖中可以看出,在frame1到frame2的過程中,使用了回彈插值器,所以從這裏也可驗證我們上面的論述:如果給當前幀添加插值器,那麽在上一幀到當前幀的進度值計算過程中會使用這個插值器。
好了,到這裏有關ofInt,ofFloat和常用的幾個函數的講解就結束了,下面我們再來看看ofObject的使用方法。

4、Keyframe之ofObject

與ofInt,ofFloat一樣,ofObject也有兩個構造函數:

public static Keyframe ofObject(float fraction)  
public static Keyframe ofObject(float fraction, Object value)  

同樣,如果使用ofObject(float fraction)來構造,也必須使用setValue(Object value)來設置這個關鍵幀所對應的值。
我們還以TextView更改字母的例子來使用下Keyframe.ofObject
效果圖如下:

技術分享圖片

明顯L前的12個字母變化的特別快,後面的14個字母變化的比較慢。
我們使用到的MyTextView,CharEvaluator都與上面的一樣,只是動畫部分不同,這裏只列出動畫的代碼:

Keyframe frame0 = Keyframe.ofObject(0f, new Character(‘A‘));  
Keyframe frame1 = Keyframe.ofObject(0.1f, new Character(‘L‘));  
Keyframe frame2 = Keyframe.ofObject(1,new Character(‘Z‘));  
  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("CharText",frame0,frame1,frame2);  
frameHolder.setEvaluator(new CharEvaluator());  
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mMyTv,frameHolder);  
animator.setDuration(3000);  
animator.start();  

在這個動畫中,我們定義了三幀:

Keyframe frame0 = Keyframe.ofObject(0f, new Character(‘A‘));  
Keyframe frame1 = Keyframe.ofObject(0.1f, new Character(‘L‘));  
Keyframe frame2 = Keyframe.ofObject(1,new Character(‘Z‘));  

frame0表示在進度為0的時候,動畫的字符是A;frame1表示在進度在0.1的時候,動畫的字符是L;frame2表示在結束的時候,動畫的字符是Z;
利用關鍵幀創建PropertyValuesHolder後,一定要記得設置自定義的Evaluator:

frameHolder.setEvaluator(new CharEvaluator());  

凡是使用ofObject來做動畫的時候,都必須調用frameHolder.setEvaluator顯示設置Evaluator,因為系統根本是無法知道,你動畫的中間值Object真正是什麽類型的。

5、疑問:如果沒有設置進度為0或者進度為1時的關鍵幀,展示是怎樣的?

首先,我們以下面這個動畫為例:

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.5f, 100f);  
Keyframe frame2 = Keyframe.ofFloat(1,0);  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", frame0,frame1,frame2);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage, frameHolder);  
animator.setDuration(3000);  
animator.start();

這裏有三個幀,在進度為0.5時,電話向右旋轉100度,然後再轉回來。
效果圖如下:

技術分享圖片

嘗試一:去掉第0幀,將以第一幀為起始位置

如果我們把第0幀去掉,只保留中間幀和結束幀,看結果會怎樣

Keyframe frame1 = Keyframe.ofFloat(0.5f, 100f);  
Keyframe frame2 = Keyframe.ofFloat(1,0);  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame1,frame2);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage, frameHolder);  
animator.setDuration(3000);  
animator.start();  

效果圖如下:

技術分享圖片

可以看到,動畫是直接從中間幀frame1開始的,即當沒有第0幀時,動畫從最近的一個幀開始。

嘗試二:去掉結束幀,將最後一幀為結束幀

如果我們把結束幀去掉,保留第0幀和中間幀,看結果會怎樣

Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.5f, 100f);  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation", frame0,frame1);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage, frameHolder);  
animator.setDuration(3000);  
animator.start();  

效果圖如下:

技術分享圖片

嘗試三:只保留一個中間幀,會崩

如果我們把第0幀和結束幀去掉,代碼如下:

PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame1);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage, frameHolder);  
animator.setDuration(3000);  
animator.start();  

在點擊按鈕開始動畫時,就直接崩了,報錯信息如下:

技術分享圖片

報錯問題是數組越界,也就是說,至少要有兩個幀才行。

嘗試四:保留兩個中間幀

再嘗試一下,如果我們把第0幀和結束幀去掉,保留兩個中間幀會怎樣:
我們在上面代碼上再加一個中間幀:

Keyframe frame1 = Keyframe.ofFloat(0.5f, 100f);  
Keyframe frame2 = Keyframe.ofFloat(0.7f,50f);  
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame1,frame2);  
  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage, frameHolder);  
animator.setDuration(3000);  
animator.start();  

效果圖如下:

技術分享圖片

可以看到,在保留兩個幀的情況下,是可以運行的,而且,由於去掉了第0幀,所以將frame1做為起始幀,又由於去掉了結束幀,所以將frame2做為結束幀。

下面我們做出結論:

  • 如果去掉第0幀,將以第一個關鍵幀為起始位置
  • 如果去掉結束幀,將以最後一個關鍵幀為結束位置
  • 使用Keyframe來構建動畫,至少要有兩個或兩個以上幀

6、開篇的電話響鈴效果

再重新看看開篇的電話響鈴的效果圖:

技術分享圖片

發現了沒,除了左右震動,圖標在震動過程中始終是放大的。
上面,我們已經實現了左右震動,下面我們再添加放大效果就好了。
框架的部分就不再講了,與上面一樣,只是動畫部分不同,先貼出動畫的完整代碼:

/** 
 * 左右震動效果 
 */  
Keyframe frame0 = Keyframe.ofFloat(0f, 0);  
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);  
Keyframe frame2 = Keyframe.ofFloat(0.2f, 20f);  
Keyframe frame3 = Keyframe.ofFloat(0.3f, -20f);  
Keyframe frame4 = Keyframe.ofFloat(0.4f, 20f);  
Keyframe frame5 = Keyframe.ofFloat(0.5f, -20f);  
Keyframe frame6 = Keyframe.ofFloat(0.6f, 20f);  
Keyframe frame7 = Keyframe.ofFloat(0.7f, -20f);  
Keyframe frame8 = Keyframe.ofFloat(0.8f, 20f);  
Keyframe frame9 = Keyframe.ofFloat(0.9f, -20f);  
Keyframe frame10 = Keyframe.ofFloat(1, 0);  
PropertyValuesHolder frameHolder1 = PropertyValuesHolder.ofKeyframe("rotation", frame0, frame1, frame2, frame3, frame4,frame5, frame6, frame7, frame8, frame9, frame10);  
  
  
/** 
 * scaleX放大1.1倍 
 */  
Keyframe scaleXframe0 = Keyframe.ofFloat(0f, 1);  
Keyframe scaleXframe1 = Keyframe.ofFloat(0.1f, 1.1f);  
Keyframe scaleXframe2 = Keyframe.ofFloat(0.2f, 1.1f);  
Keyframe scaleXframe3 = Keyframe.ofFloat(0.3f, 1.1f);  
Keyframe scaleXframe4 = Keyframe.ofFloat(0.4f, 1.1f);  
Keyframe scaleXframe5 = Keyframe.ofFloat(0.5f, 1.1f);  
Keyframe scaleXframe6 = Keyframe.ofFloat(0.6f, 1.1f);  
Keyframe scaleXframe7 = Keyframe.ofFloat(0.7f, 1.1f);  
Keyframe scaleXframe8 = Keyframe.ofFloat(0.8f, 1.1f);  
Keyframe scaleXframe9 = Keyframe.ofFloat(0.9f, 1.1f);  
Keyframe scaleXframe10 = Keyframe.ofFloat(1, 1);  
PropertyValuesHolder frameHolder2 = PropertyValuesHolder.ofKeyframe("ScaleX",scaleXframe0,scaleXframe1,scaleXframe2,scaleXframe3,scaleXframe4,scaleXframe5,scaleXframe6,scaleXframe7,scaleXframe8,scaleXframe9,scaleXframe10);  
  
  
/** 
 * scaleY放大1.1倍 
 */  
Keyframe scaleYframe0 = Keyframe.ofFloat(0f, 1);  
Keyframe scaleYframe1 = Keyframe.ofFloat(0.1f, 1.1f);  
Keyframe scaleYframe2 = Keyframe.ofFloat(0.2f, 1.1f);  
Keyframe scaleYframe3 = Keyframe.ofFloat(0.3f, 1.1f);  
Keyframe scaleYframe4 = Keyframe.ofFloat(0.4f, 1.1f);  
Keyframe scaleYframe5 = Keyframe.ofFloat(0.5f, 1.1f);  
Keyframe scaleYframe6 = Keyframe.ofFloat(0.6f, 1.1f);  
Keyframe scaleYframe7 = Keyframe.ofFloat(0.7f, 1.1f);  
Keyframe scaleYframe8 = Keyframe.ofFloat(0.8f, 1.1f);  
Keyframe scaleYframe9 = Keyframe.ofFloat(0.9f, 1.1f);  
Keyframe scaleYframe10 = Keyframe.ofFloat(1, 1);  
PropertyValuesHolder frameHolder3 = PropertyValuesHolder.ofKeyframe("ScaleY",scaleYframe0,scaleYframe1,scaleYframe2,scaleYframe3,scaleYframe4,scaleYframe5,scaleYframe6,scaleYframe7,scaleYframe8,scaleYframe9,scaleYframe10);  
  
/** 
 * 構建動畫 
 */  
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage, frameHolder1,frameHolder2,frameHolder3);  
animator.setDuration(1000);  
animator.start();  

這裏的總共分為四步:
第一步,實現左右震鈴效果;
這部分代碼前面已經講過,這裏就不再贅述
第二步,利用View類中的SetScaleX(float value)方法所對應的ScaleX屬性,在動畫過程中,將圖片橫向放大1.1倍:

Keyframe scaleXframe0 = Keyframe.ofFloat(0f, 1);  
Keyframe scaleXframe1 = Keyframe.ofFloat(0.1f, 1.1f);  
Keyframe scaleXframe2 = Keyframe.ofFloat(0.2f, 1.1f);  
Keyframe scaleXframe3 = Keyframe.ofFloat(0.3f, 1.1f);  
Keyframe scaleXframe4 = Keyframe.ofFloat(0.4f, 1.1f);  
Keyframe scaleXframe5 = Keyframe.ofFloat(0.5f, 1.1f);  
Keyframe scaleXframe6 = Keyframe.ofFloat(0.6f, 1.1f);  
Keyframe scaleXframe7 = Keyframe.ofFloat(0.7f, 1.1f);  
Keyframe scaleXframe8 = Keyframe.ofFloat(0.8f, 1.1f);  
Keyframe scaleXframe9 = Keyframe.ofFloat(0.9f, 1.1f);  
Keyframe scaleXframe10 = Keyframe.ofFloat(1, 1);  

非常註意的是,在動畫過程中放大1.1倍,在開始動畫和動畫結束時,都要還原狀態,即原大小的1倍值:

Keyframe scaleXframe0 = Keyframe.ofFloat(0f, 1);  
Keyframe scaleXframe10 = Keyframe.ofFloat(1, 1);  

第三步,同樣利用View類的SetScaleY(float value)方法,在動畫過程中將圖片縱向放大1.1倍。原理與scaleX相同,就不再細講。
第四步:生成ObjectAnimator實例:

Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage, frameHolder1,frameHolder2,frameHolder3);  

我們前面講過,ObjectAnimator ofPropertyValuesHolder(Object target,PropertyValuesHolder… values)中第二個參數是可變長參數,可以傳進去任何多個PropertyValuesHolder對象,這些對象所對應的動畫會同時作用於控件上。這裏我們就將三個屬性動畫同時作用在mImage上,所以圖片的動畫就表現為在左右震動的同時,橫向放大1.1倍,縱向也放大了1.1倍。

所以說,借助Keyframe,不需要使用AnimatorSet,也能實現多個動畫同時播放。這也是ObjectAnimator中唯一一個能實現多動畫同時播放的方法,其它的ObjectAnimator.ofInt,ObjectAnimator.ofFloat,ObjectAnimator.ofObject都只能實現針對一個屬性動畫的操作!

三、PropertyValuesHolder之其它函數

PropertyValuesHolder除了上面的講到的ofInt,ofFloat,ofObject,ofKeyframe以外,api 11的還有幾個函數:

/** 
 * 設置動畫的Evaluator 
 */  
public void setEvaluator(TypeEvaluator evaluator)  
/** 
 * 用於設置ofFloat所對應的動畫值列表 
 */  
public void setFloatValues(float... values)  
/** 
 * 用於設置ofInt所對應的動畫值列表 
 */  
public void setIntValues(int... values)  
/** 
 * 用於設置ofKeyframe所對應的動畫值列表 
 */  
public void setKeyframes(Keyframe... values)  
/** 
 * 用於設置ofObject所對應的動畫值列表 
 */  
public void setObjectValues(Object... values)  
/** 
 * 設置動畫屬性名 
 */  
public void setPropertyName(String propertyName)  

這些函數都比較好理解,setFloatValues(float… values)對應PropertyValuesHolder.ofFloat(),用於動態設置動畫中的數值。setIntValues、setKeyframes、setObjectValues同理;
setPropertyName用於設置PropertyValuesHolder所需要操作的動畫屬性名;
最重要的是setEvaluator(TypeEvaluator evaluator)

/** 
 * 設置動畫的Evaluator 
 */  
public void setEvaluator(TypeEvaluator evaluator)  

如果是利用PropertyValuesHolder.ofObject()來創建動畫實例的話,我們是一定要顯示調用 PropertyValuesHolder.setEvaluator()來設置Evaluator的。在上面的字母轉換的例子中,我們已經用過這個函數了。這裏也就沒什麽好講的了。

本文轉自:自定義控件三部曲之動畫篇(八)——PropertyValuesHolder與Keyframe

android.animation(5) - PropertyValuesHolder與Keyframe(轉)