1. 程式人生 > >Android 動畫學習小結

Android 動畫學習小結

2.3以及以前的版本支援3種類型的動畫:逐幀動畫,佈局動畫,檢視動畫。佈局動畫,檢視動畫合稱為補間動畫。

3.0後推出了屬性動畫

一.逐幀動畫

逐幀動畫就像動畫片一樣把一些圖片組合並且快速播放,好像物體在運動一樣。

建立逐幀動畫,使用AnimationDrawable這個類

顯示動畫的步驟:

1.在佈局檔案中加入imageview用於顯示動畫

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >
    
    <Button 
        android:id="@+id/start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="start"
        />
    
    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />
</LinearLayout>

2.準備一些連續播放起來可以形成“動畫”的圖片,且稱為幀圖片,然後構造一個xml來宣告這些幀圖片


loading.xml

<?xml version="1.0" encoding="UTF-8"?>
<animation-list android:oneshot="false"
	xmlns:android="http://schemas.android.com/apk/res/android">
	<item android:duration="100" android:drawable="@drawable/loading_00" />
	<item android:duration="100" android:drawable="@drawable/loading_01" />
	<item android:duration="100" android:drawable="@drawable/loading_02" />
	<item android:duration="100" android:drawable="@drawable/loading_03" />
	<item android:duration="100" android:drawable="@drawable/loading_04" />
	<item android:duration="100" android:drawable="@drawable/loading_05" />
	<item android:duration="100" android:drawable="@drawable/loading_06" />
	<item android:duration="100" android:drawable="@drawable/loading_07" />
</animation-list> 
android:oneshot="false"表明動畫連續播放,android:duration表明持續時間100毫秒

3.程式碼中設定動畫

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		 Button start = (Button) findViewById(R.id.start);
	   	 ImageView imgView = (ImageView)findViewById(R.id.img);
	   	 imgView.setBackgroundResource(R.anim.loading);
	
	    final AnimationDrawable frameAnimation = 
	   		 (AnimationDrawable) imgView.getBackground();
	   	 
	    start.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
		    	 if (frameAnimation.isRunning()) {
		    		//動畫暫停
		        	 frameAnimation.stop();
		    	 }
		    	 else {
		    		 //動畫啟動
		        	 frameAnimation.start();
		    	 }
			}
		 });
	}
}

執行程式,點選按鈕,可以看到進度條無限轉動的畫面。

以上轉動效果還可以用progressbar實現

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:indeterminateDrawable="@anim/loading" />

二.補間動畫(佈局動畫,檢視動畫)

補間動畫的型別:

縮放動畫:沿x軸或者y軸縮小或者放大檢視,也可以指定一個支點並圍繞該支點來縮放動畫

旋轉動畫:圍繞一個支點將檢視旋轉一定角度

平移動畫:沿x或者y軸移動檢視

透明度動畫:更改檢視的透明度

(1).佈局動畫

佈局動畫作用於ViewGroup的子節點(LinearLayout下的view,或者ListVIew, GridView等的item),為什麼叫佈局動畫,大概是因為在佈局檔案中實現吧

這裡演示如何在xml檔案中建立,下面提到的xml檔案都放在anim資料夾下。步驟如下:

1.建立動畫的xml檔案,節點名稱根據你想要的效果(縮放,旋轉,平移,透明度)而選擇

縮放動畫scale.xml:

   <scale 
         android:fromXScale="1"
         android:toXScale="1"
         android:fromYScale="0.1"
         android:toYScale="1.0"
         android:duration="500"
         android:pivotX="50%"
         android:pivotY="50%"
         android:startOffset="100" />

屬性說明:

android:fromXScale="1"
android:toXScale="1"

指定在x軸上的放大係數變化值,從1到1表明不變,y軸同理

android:duration="500" 縮放時間500毫秒

android:pivotX="50%"

android:pivotY="50%"

在中間時刻,物件在x,y方向上都為50%

android:startOffset="100" 在動畫開始前等待100毫秒

旋轉動畫rotate.xml:

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
       android:interpolator="@android:anim/accelerate_interpolator"
       android:fromDegrees="0.0" 
      android:toDegrees="360"
      android:pivotX="50%"
      android:pivotY="50%"
      android:duration="500" />

屬性說明:角度從0到360度旋轉,中間時刻旋轉一半形度,一次旋轉時間500毫秒

android:interpolator="@android:anim/accelerate_interpolator"

指定動畫所用的插值器是系統自帶的加速插值器,插值器指定了動畫的某個屬性,比如角度如何隨時間變化,是否是勻速旋轉,還是越轉越快?這裡是越轉越快

平移動畫translate.xml:

<translate xmlns:android="http://schemas.android.com/apk/res/android"
       android:fromYDelta="-100%" android:toYDelta="0"
      android:duration="500" />

透明度動畫alpha.xml:

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />
屬性說明:透明度從0到1,漸變持續1秒

2.amin資料夾下建立xml,節點名稱為layoutAnimation,屬性中宣告使用的動畫:

layout_controller.xml

<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/rotate" 
        />
android:animation="@anim/rotate,此佈局動畫使用了旋轉動畫

3.在主頁面佈局中引用此佈局動畫的xml

<?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"
    android:layoutAnimation="@anim/layout_controller"
    >
    <TextView
      android:id="@+id/list_view_id"
      android:text="sadas"
      android:textSize="40sp"
      android:gravity="center"
      android:layout_gravity="center"
      android:persistentDrawingCache="animation|scrolling"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
       />
</LinearLayout>
android:layoutAnimation="@anim/layout_controller聲明瞭上面的layout_controller.xml。現在執行程式,可以看到textview載入的時候運行了旋轉的動畫

android:persistentDrawingCache="animation|scrolling 可以提高效率

動畫之間還可以組合,相當於把動畫元素放入集合類中,像這樣:

<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator">
    <translate android:fromYDelta="-100%" android:toYDelta="0" android:duration="500" />
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" />
</set>

引用它的xml:
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="30%"
        android:animationOrder="reverse"
        android:animation="@anim/alpha_translate" 
        />

要使得2個Activity切換時播放動畫,可以套用以上幾種型別的動畫xml,最後在程式碼中加上這句程式碼就行。

            startActivity(intent);  
            //第一個引數為啟動時動畫效果,第二個引數為退出時動畫效果  
            overridePendingTransition(R.anim.fade, R.anim.hold);  


(2)檢視動畫

佈局動畫實現的效果檢視動畫都可以實現,檢視動畫使用純程式碼製作動畫,可以完成比佈局動畫更復雜的效果(矩陣變換等),但是實現複雜度也更大。

先看一個簡單的實現,2個步驟,

1.實現動畫類ViewAnimation2,設定動畫的形式

2.呼叫佈局總view的startAnimation方法將此動畫設定給view。

activity:

public class ViewAnimationActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_layout);
        setupListView();
        this.setupButton();
    }
    private void setupListView()
    {
    	  String[] listItems = new String[] {
    	        "Item 1", "Item 2", "Item 3",
    	        "Item 4", "Item 5", "Item 6",
    	  };
    	  
    	  ArrayAdapter<String> listItemAdapter = 
    		   new ArrayAdapter<String>(this
    		           ,android.R.layout.simple_list_item_1
    		           ,listItems);
    	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
    	  lv.setAdapter(listItemAdapter);
    }
    private void setupButton()
    {
       Button b = (Button)this.findViewById(R.id.btn_animate);
       b.setOnClickListener(
           new Button.OnClickListener(){
             public void onClick(View v)
             {
                animateListView();
             }
           });
    }
    private void animateListView()
    {
  	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
  	  lv.startAnimation(new ViewAnimation2());
    }
}

動畫實現 ViewAnimation2:
public class ViewAnimation2 extends Animation 
{
    public ViewAnimation2(){}

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        setDuration(2500);
        setFillAfter(true);
        setInterpolator(new LinearInterpolator());
    }
    //interpolatedTime值從0到1
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final Matrix matrix = t.getMatrix();
        matrix.setScale(interpolatedTime, interpolatedTime);
    }
}
initialize方法是初始化時的回撥方法,設定動畫的一些屬性(持續時間,插值器等)

applyTransformation方法在動畫執行的過程中被反覆呼叫,android反覆呼叫這個方法來模擬動畫,interpolatedTime引數根據initialize方法中設定的時間,從0到1變化,每次回撥時的值都不同。當interpolatedTime值增長到1時,動畫播放結束。可以利用applyTransformation這個方法進行物件的矩陣變換,以形成動畫。以上的例子是根據時間逐漸增長listview的大小。

setInterpolator(new LinearInterpolator())指定動畫勻速執行,matrix.setScale(interpolatedTime, interpolatedTime)讓view的大小隨時間增長變大。

稍微改進一下上面的動畫:

public class ViewAnimationActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_layout);
        setupListView();
        this.setupButton();
    }
    private void setupListView()
    {
    	  String[] listItems = new String[] {
    	        "Item 1", "Item 2", "Item 3",
    	        "Item 4", "Item 5", "Item 6",
    	  };
    	  
    	  ArrayAdapter<String> listItemAdapter = 
    		   new ArrayAdapter<String>(this
    		           ,android.R.layout.simple_list_item_1
    		           ,listItems);
    	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
    	  lv.setAdapter(listItemAdapter);
    }
    private void setupButton()
    {
       Button b = (Button)this.findViewById(R.id.btn_animate);
       b.setOnClickListener(
           new Button.OnClickListener(){
             public void onClick(View v)
             {
                animateListView();
             }
           });
    }
    private void animateListView()
    {
  	  ListView lv = (ListView)this.findViewById(R.id.list_view_id);
  	  float currX = (float) (lv.getHeight() / 2.0);
  	  float currY = (float) (lv.getWidth() / 2.0);
//  	  lv.startAnimation(new ViewAnimation2());
  	  lv.startAnimation(new ViewAnimation(currX, currY));
    }
public class ViewAnimation extends Animation {
	float centerX, centerY;
    public ViewAnimation(float cx, float cy)
    {
    	centerX = cx;
    	centerY = cy;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        setDuration(2500);
        setFillAfter(true);
        setInterpolator(new LinearInterpolator());
        
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
    	applyTransformationNew(interpolatedTime,t);
    }
    
    protected void applyTransformationNew(float interpolatedTime, Transformation t) 
    {
        final Matrix matrix = t.getMatrix();
        matrix.setScale(interpolatedTime, interpolatedTime);
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

注意到applyTransformationNew方法中多了preTranslate和postTranslate,相當於在動畫縮放前後進行了位置平移。

檢視動畫其他更豐富的實現效果有待深入研究。

三.屬性動畫

舊的動畫API位於包android.view.animation中,新的動畫API位於包android.animation中

屬性動畫主要有以下一些特性

ValueAnimator 值動畫生成器
ObjectAnimator 物件動畫生成器
ViewPropertyAnimator 檢視屬性動畫生成器
AnimatorSet 動畫生成器集合
Animation Listeners 動畫生成器監聽
TypeEvaluator 型別求值器
PropertyValuesHolder 屬性值持有者
Interpolators 插值器
Keyframes 關鍵幀
Layout Changes 佈局轉變
Declaring Animations in XML  xml載入動畫

1.ValueAnimator 值動畫生成器

ValueAnimator持續跟蹤動畫的狀態,比如動畫執行時間和某個屬性的值,它包含一個時間插值器TimeInterpolator,定義了動畫的插值器。還包含一個型別求值器TypeEvaluator ,定義瞭如何計算動畫的屬性值。比如在下圖中,view的座標隨著時間的增加而以整數增加,並且是加速增長的,在這個動畫中,插值器是AccelerateDecelerateInterpolator,型別求值器是IntEvaluator


看一下API GUIDE上給出的示意圖:

對應的程式碼以及解釋:

    	ValueAnimator anim = ValueAnimator.ofInt(10, 200);
    	anim.setDuration(5000);
    	anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
			
			@Override
			public void onAnimationUpdate(ValueAnimator animation) {
				// TODO Auto-generated method stub
	         int value = (Integer) animation.getAnimatedValue();
			}
		});
    	anim.start();

ValueAnimator的例項設定持續時間duration,開始,結束值startPropertyValue和endPropertyValue,ValueAnimator.AnimatorUpdateListener監聽屬性值的變化,onAnimationUpdate方法在動畫執行過程中不斷被回撥,animation.getAnimatedValue獲得某個時間點上的屬性值

2.ObjectAnimator物件動畫生成器 ObjectAnimator是ValueAnimator的子類,構造和其類似,不過多了個property的引數 以下是一個TextView的淡入動畫效果的實現
	        ObjectAnimator fadeOut = 
	        	ObjectAnimator.ofFloat(tv, "alpha", 0f, 100f);
	        fadeOut.setDuration(5000);
	        fadeOut.start();

其中tv是個textview例項,alpha是屬性,這個屬性不是隨便設定的,是因為textview中存在setAlpha這個方法(繼承自view),如果你給這個引數指定了一個值,那麼實現動畫的物件中必須存在對應的set的方法。可以傳入的引數有:

translationX
translationY
rotation
rotationX
rotationY
scaleX
scaleY
pivotX
pivotY

y
alpha

3.AnimatorSet 動畫生成器集合

AnimatorSet可以把多個動畫組合在一起,並且決定他們的播放順序

textview先淡出再淡入的效果:

    	m_tv.setAlpha(1f);
        ObjectAnimator fadeOut = 
        	ObjectAnimator.ofFloat(tv, "alpha", 0f);
        ObjectAnimator fadeIn = 
        	ObjectAnimator.ofFloat(tv, "alpha", 1f);
        AnimatorSet as = new AnimatorSet();
        as.playSequentially(fadeOut,fadeIn);
        as.setDuration(5000); 
        as.start();
這裡new 了一個AnimatorSet物件,並且將2個ObjectAnimator物件當作引數傳入playSequentially方法,使它們順序執行

API GUIDE上的一個例子:

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
這段程式碼的作用是先後播放2個AnimatorSet,播放的動畫順序是bounceAnim->squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2(同時)->bounceBackAnim->fadeAnim

4.ViewPropertyAnimator檢視屬性動畫生成器

ViewPropertyAnimator有些類似於AnimatorSet,它提供了更簡便的方法在動畫中同時改變view的屬性

下面的程式碼作用是把textview  從螢幕的右下角移動到原來的位置

    	m_tv.setAlpha(1f);
    	float h = m_tv.getHeight();
    	float w = m_tv.getWidth();
    	float x = m_tv.getX();
    	float y = m_tv.getY();
    
    	m_tv.setX(w);
    	m_tv.setY(h);
    	
    	ViewGroup layout = (ViewGroup)m_tv.getParent();
    	layout.setClipChildren(true);
    	
    	ViewPropertyAnimator vpa = m_tv.animate();
    	vpa.x(x);
    	vpa.y(y);
    	
        vpa.setDuration(5000); 
        vpa.setInterpolator(
        		new  AccelerateDecelerateInterpolator());
5.動畫監聽Animation Listeners

Animator.AnimatorListener 監聽開始,結束,重複,退出等事件

ValueAnimator.AnimatorUpdateListener 在上面ValueAnimator的例子中演示過,在動畫執行過程中不斷回撥,可以監聽動畫屬性值的變化
6.TypeEvaluator 型別求值器

Animator提供ofInt,ofFloat來設定動畫屬性的變化範圍,如果想實現自己的變化,可以實現TypeEvaluator介面

public class FloatEvaluator implements TypeEvaluator {

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

7.PropertyValuesHolder 屬性值持有者

PropertyValuesHolder用來在動畫中儲存一個或者多個view的屬性名/目標值,用來生成動畫。以下的程式碼和上面ViewPropertyAnimator的例子實現的是一樣的動畫效果

    	m_tv.setAlpha(1f);
    	float h = m_tv.getHeight();
    	float w = m_tv.getWidth();
    	float x = m_tv.getX();
    	float y = m_tv.getY();
    
    	m_tv.setX(w);
    	m_tv.setY(h);
    	PropertyValuesHolder pvhX = 
    		PropertyValuesHolder.ofFloat("x", x);

    	PropertyValuesHolder pvhY = 
    		PropertyValuesHolder.ofFloat("y", y);
    	
    	ObjectAnimator oa
    	= ObjectAnimator.ofPropertyValuesHolder(m_tv, pvhX, pvhY);
        oa.setDuration(5000);
        oa.setInterpolator(
        		new  AccelerateDecelerateInterpolator());
        oa.start();
8.Keyframes 關鍵幀

Keyframes由時間/值的鍵值對組成,顧名思義,就是指定動畫一些關鍵時間點上的屬性值,它可以和PropertyValuesHolder配合使用

下面的程式碼作用是,動畫執行時,textview從右下移動到左上,當時間到達20%時,透明度變為80%,一半時間時,透明度變為20%,到80%的時間時,透明度變回80%

    	m_tv.setAlpha(1f);
    	
    	float h = m_tv.getHeight();
    	float w = m_tv.getWidth();
    	float x = m_tv.getX();
    	float y = m_tv.getY();
        //3個時間點不同的透明度設定
    	Keyframe kf0 = Keyframe.ofFloat(0.2f, 0.8f);
    	Keyframe kf1 = Keyframe.ofFloat(.5f, 0.2f);
    	Keyframe kf2 = Keyframe.ofFloat(0.8f, 0.8f);    	
    	PropertyValuesHolder pvhAlpha = 
    		PropertyValuesHolder.ofKeyframe("alpha", kf0, kf1, kf2);
    	
    	PropertyValuesHolder pvhX = 
    		PropertyValuesHolder.ofFloat("x", w, x);
    	
    	//end frame
        ObjectAnimator anim = 
        	ObjectAnimator.ofPropertyValuesHolder(m_tv, pvhAlpha,pvhX);
        anim.setDuration(5000);
        anim.start();
9.Layout Changes 佈局轉變

屬性動畫的API可以在ViewGroup上設定動畫,系統為ViewGroup的子view的顯示和消失都設定了預設的動畫,要啟用它們只要在xml檔案中加上

android:animateLayoutChanges="true"

下面程式碼展示了gridlayout中按鈕增加/刪除的預設動畫

public class MainActivity extends Activity {

    private int numButtons = 1;

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_animations_by_default);

        final GridLayout gridContainer = (GridLayout) findViewById(R.id.gridContainer);

        Button addButton = (Button) findViewById(R.id.addNewButton);
        addButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Button newButton = new Button(MainActivity.this);
                newButton.setText(String.valueOf(numButtons++));
                newButton.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        gridContainer.removeView(v);
                    }
                });
                gridContainer.addView(newButton, Math.min(1, gridContainer.getChildCount()));
            }
        });
    }

}
佈局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Add Button"
        android:id="@+id/addNewButton"
        />
    <GridLayout
        android:columnCount="4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/gridContainer"
        android:animateLayoutChanges="true"
        />
</LinearLayout>
也可以自己設定相應的效果:
        LinearLayout yourLayout = (LinearLayout)findViewById(R.layout.xx);
        LayoutTransition lt = new LayoutTransition();
        yourLayout.setLayoutTransition(lt);
        Animator a = lt.getAnimator(LayoutTransition.APPEARING);
        ObjectAnimator oa = ..... //自定義的ObjectAnimator
        lt.setAnimator(LayoutTransition.APPEARING, oa);

10.Declaring Animations in XML 在xml中定義動畫

屬性動畫也可以用xml定義動畫,但是xml檔案不是放在res/anim下,而是放在res/animator下

標籤對應的動畫類:

ValueAnimator - <animator>
ObjectAnimator - <objectAnimator>
AnimatorSet - <set>

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

要使xml定義的動畫生效,需要加上以下程式碼:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.anim.property_animator);
set.setTarget(myObject);
set.start();