Android 中最實用的動畫效果實現

提供了三種動畫效果:逐幀動畫(frame-by-frame animation),這種動畫和GIF一樣,一幀一幀的顯示來組成動畫效果;佈局動畫(layout animation),這種動畫用來設置layout內的所有UI控件;控件動畫(view animation),這種是應用到具體某個view上的動畫。

 
在這三種動畫實現中逐幀動畫是最簡單的,而控件動畫是有點複雜的,要涉及到線性代數中的矩陣運算,下面就由易到難逐個介紹,先來看看逐幀動畫如何實現。
 
逐幀動畫
逐幀動畫是通過OPhone中的android.graphics.drawable.AnimationDrawable類來實現的,在該類中保存了幀序列以及顯示的時間,爲了簡化動畫的創建OPhone提供了一種通過XML來創建逐幀動畫的方式,這樣把動畫的創建和代碼分來以後如果需要修改動畫內容,只需要修改資源文件就可以了不用修改代碼,簡化開發維護工作。在res/drawable/文件夾下創建一個XML文件,下面是一個示例文件(res\drawable\qq_animation.xml):
  1. <animation-list    
  2.     xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:oneshot="false">   
  4. <item android:drawable="@drawable/qq001" android:duration="80"/>   
  5. <item android:drawable="@drawable/qq002" android:duration="80"/>   
  6. <item android:drawable="@drawable/qq003" android:duration="80"/>   
  7. <item android:drawable="@drawable/qq004" android:duration="80"/>   
  8. <item android:drawable="@drawable/qq005" android:duration="80"/>   
  9. <item android:drawable="@drawable/qq006" android:duration="80"/>   
  10. <item android:drawable="@drawable/qq007" android:duration="80"/>   
  11. <item android:drawable="@drawable/qq008" android:duration="80"/>   
  12. </animation-list>  
Java代碼  收藏代碼
  1. <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/qq001" android:duration="80"/> <item android:drawable="@drawable/qq002" android:duration="80"/> <item android:drawable="@drawable/qq003" android:duration="80"/> <item android:drawable="@drawable/qq004" android:duration="80"/> <item android:drawable="@drawable/qq005" android:duration="80"/> <item android:drawable="@drawable/qq006" android:duration="80"/> <item android:drawable="@drawable/qq007" android:duration="80"/> <item android:drawable="@drawable/qq008" android:duration="80"/> </animation-list>  
 
 
在上面的定義中通過animation-list來指定這是個AnimationDrawable動畫定義,裏面的item來指定每幀圖片和顯示時間(單位爲毫秒),幀顯示的順序就是item定義的順序。如果android:oneshot設置爲true表明該動畫只播放一次,否則該動畫會循環播放。這些設置也可以通過AnimationDrawable提供的函數來設置。動畫中引用的文件爲QQ表情文件,包含在示例項目代碼中。
然後在layout中定義一個ImageView來顯示上面定義的AnimationDrawable,layout代碼如下(res\layout\main.xml):
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <LinearLayout    
  3. xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:orientation="vertical"    
  5.     android:layout_width="fill_parent"  
  6.     android:layout_height="fill_parent">   
  7.     <ImageView    
  8.        android:id="@+id/animation_view"  
  9.        android:layout_width="fill_parent"    
  10.        android:layout_height="wrap_content"  
  11.        android:src="@drawable/qq_animation" />   
  12.     <Button    
  13.        android:id="@+id/animation_btn"    
  14.        android:layout_width="fill_parent"  
  15.        android:layout_height="wrap_content"    
  16.        android:text="@string/start_animation" />   
  17.     <Button    
  18.        android:id="@+id/one_shot_btn"    
  19.        android:layout_width="fill_parent"  
  20.        android:layout_height="wrap_content"    
  21.        android:text="@string/play_once" />   
  22. </LinearLayout>  
Java代碼  收藏代碼
  1. <?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"> <ImageView android:id="@+id/animation_view" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/qq_animation" /> <Button android:id="@+id/animation_btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/start_animation" /> <Button android:id="@+id/one_shot_btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/play_once" /> </LinearLayout>  
注意這裏的ImageView 通過android:src="@drawable/qq_animation"引用了前面定義的AnimationDrawable,下面是兩個按鈕用來控制播放動畫和設置AnimationDrawable的oneshot屬性。
下面就是控制動畫播放的類代碼(src\org\goodev\animation\AnimActivity.java):
 
  1. public class AnimActivity extends Activity {   
  2.     /** Called when the activity is first created. */  
  3.     AnimationDrawable mAd;   
  4.     Button mPlayBtn;   
  5.     Button mOneShotBtn;   
  6.     boolean mIsOneShot;   
  7.     
  8.     @Override  
  9.     public void onCreate(Bundle savedInstanceState) {   
  10.        super.onCreate(savedInstanceState);   
  11.        setContentView(R.layout.main);   
  12.     
  13.        ImageView iv = (ImageView) findViewById(R.id.animation_view);   
  14.        mAd = (AnimationDrawable) iv.getDrawable();   
  15.     
  16.        mPlayBtn = (Button) findViewById(R.id.animation_btn);   
  17.        mPlayBtn.setOnClickListener(new OnClickListener() {   
  18.            @Override  
  19.            public void onClick(View view) {   
  20.               startAnimation();   
  21.            }   
  22.        });   
  23.     
  24.        mOneShotBtn = (Button) findViewById(R.id.one_shot_btn);   
  25.        mOneShotBtn.setOnClickListener(new OnClickListener() {   
  26.            @Override  
  27.            public void onClick(View view) {   
  28.               if (mIsOneShot) {   
  29.                   mOneShotBtn.setText("Play Once");   
  30.               } else {   
  31.                   mOneShotBtn.setText("Play Repeatly");   
  32.               }   
  33.               mAd.setOneShot(!mIsOneShot);   
  34.               mIsOneShot = !mIsOneShot;   
  35.            }   
  36.        });   
  37.     
  38.     }   
  39.     
  40.     /**  
  41.      * 通過AnimationDrawable的start函數播放動畫,  
  42.      * stop函數停止動畫播放,  
  43.      * isRunning來判斷動畫是否正在播放。  
  44.      */  
  45.     public void startAnimation() {   
  46.        if (mAd.isRunning()) {   
  47.            mAd.stop();   
  48.        } else {   
  49.            mAd.stop();   
  50.            mAd.start();   
  51.        }   
  52.     }   
  53. }   
  54.    
Java代碼  收藏代碼
  1. public class AnimActivity extends Activity { /** Called when the activity is first created. */ AnimationDrawable mAd; Button mPlayBtn; Button mOneShotBtn; boolean mIsOneShot; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ImageView iv = (ImageView) findViewById(R.id.animation_view); mAd = (AnimationDrawable) iv.getDrawable(); mPlayBtn = (Button) findViewById(R.id.animation_btn); mPlayBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { startAnimation(); } }); mOneShotBtn = (Button) findViewById(R.id.one_shot_btn); mOneShotBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (mIsOneShot) { mOneShotBtn.setText("Play Once"); } else { mOneShotBtn.setText("Play Repeatly"); } mAd.setOneShot(!mIsOneShot); mIsOneShot = !mIsOneShot; } }); } /** * 通過AnimationDrawable的start函數播放動畫, * stop函數停止動畫播放, * isRunning來判斷動畫是否正在播放。 */ public void startAnimation() { if (mAd.isRunning()) { mAd.stop(); } else { mAd.stop(); mAd.start(); } } }  
 
佈局動畫介紹
 
佈局動畫和逐幀動畫是由本質的不同的,逐幀動畫是一幀幀圖片組成的,而佈局動畫是漸變動畫,OPhone通過改變UI的屬性(大小、位置、透明度等)來實現動畫效果。
 
在OPhone顯示系統中,每個view都對應一個矩陣來控制該view顯示的位置,通過不同的方式來改變該控制矩陣就可以實現動畫效果,例如旋轉、移動、縮放等。
 
不同的矩陣變換有不同的類來實現,android.view.animation.Animation類代表所有動畫變換的基類,目前在OPhone系統中有如下五個實現(都位於android.view.animation包中):
  • l AlphaAnimation:實現alpha漸變,可以使界面逐漸消失或者逐漸顯現
  • l TranslateAnimation:實現位置移動漸變,需要指定移動的開始和結束座標
  • l ScaleAnimation: 實現縮放漸變,可以指定縮放的參考點
  • l RotateAnimation:實現旋轉漸變,可以指定旋轉的參考點,默認值爲(0,0)左上角。
  • l AnimationSet: 代表上面的漸變組合
有一個和漸變動畫效果關係比較密切的類android.view.animation.Interpolator,該類定義了漸變動畫改變的速率,可以設置爲加速變化、減速變化或者重複變化。關於Interpolator詳細信息請參考文檔介紹。另外在OPhone SDK中的android.jar文件中也有各種interpolator的定義,感興趣的可以參考android.jar中的\res\anim目錄中的文件。
 
動畫的實現及應用
上面這些漸變方式也可以在XML文件中定義,這些文件位於res\anim目錄下。根元素爲set代表AnimationSet,裏面可以有多個漸變定義,如下是alpha漸變的定義(res\anim\alpha_anim.xml):
  1. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  2. android:interpolator="@android:anim/decelerate_interpolator">   
  3. <alpha    
  4.     android:fromAlpha="0.0"    
  5.     android:toAlpha="1.0"    
  6.     android:duration="1000" />   
  7. </set>  
Java代碼  收藏代碼
  1. <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator"> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" /> </set>  
如果只定義一種漸變效果,則可以去除set元素,如下:
 
  1. <alpha    
  2. xmlns:android="http://schemas.android.com/apk/res/android"  
  3. android:interpolator="@android:anim/accelerate_interpolator"  
  4. android:fromAlpha="0.0"    
  5. android:toAlpha="1.0"    
  6. android:duration="1000" />  
Java代碼  收藏代碼
  1. <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="1000" />  
 
上面定義的alpha從0(透明)到1(不透明)漸變,漸變時間爲1000毫秒。
 
  1. <scale    
  2. xmlns:android="http://schemas.android.com/apk/res/android"  
  3. android:interpolator="@android:anim/accelerate_interpolator"  
  4. android:fromXScale="1"  
  5. android:toXScale="1"  
  6. android:fromYScale="0.1"  
  7. android:toYScale="1.0"  
  8. android:duration="500"  
  9. android:pivotX="50%"  
  10. android:pivotY="50%"  
  11. android:startOffset="100" />  
Java代碼  收藏代碼
  1. <scale xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" 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" />  
 
上面是一個縮放漸變的定義,from... 和 to... 分別定義縮放漸變的開始和結束的縮放倍數,上面定義X軸都爲1不縮放,而Y軸從0.1到1逐漸放大(開始高度爲正常大小的0.1然後逐漸放大到正常大小)。縮放持續的時間爲500毫秒,縮放的中心點(通過pivotX,pivotY定義)在控件的中間位置。startOffset指定了在縮放開始前等待的時間。
  1. <rotate    
  2. xmlns:android="http://schemas.android.com/apk/res/android"  
  3. android:interpolator="@android:anim/accelerate_interpolator"  
  4. android:fromDegrees="0.0"  
  5. android:toDegrees="360"  
  6. android:pivotX="50%"  
  7. android:pivotY="50%"  
  8. android:duration="500" />  
Java代碼  收藏代碼
  1. <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毫秒,變換的中心點位控件的中心位置。
  1. <translate    
  2. xmlns:android="http://schemas.android.com/apk/res/android"  
  3. android:interpolator="@android:anim/accelerate_interpolator"  
  4. android:fromYDelta="-100%"    
  5. android:toYDelta="0"  
  6. android:duration="500" />  
Java代碼  收藏代碼
  1. <translate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromYDelta="-100%" android:toYDelta="0" android:duration="500" />  
 
 
上面定義了位置變換,實現一種下落的效果。
 
把上面不同的定義放到set中就可以實現不同的組合動畫效果了,下面的示例實現了控件在下落的過程中逐漸顯示的效果(res\anim\translate_alpha_anim.xml):
  1. <set xmlns:android="http://schemas.android.com/apk/res/android"  
  2. android:interpolator="@android:anim/decelerate_interpolator">   
  3. <translate    
  4. android:fromYDelta="-100%"    
  5. android:toYDelta="0"  
  6. android:duration="500" />   
  7. <alpha    
  8. android:fromAlpha="0.0"    
  9. android:toAlpha="1.0"  
  10. android:duration="500" />   
  11. </set>  
Java代碼  收藏代碼
  1. <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator"> <translate android:fromYDelta="-100%" android:toYDelta="0" android:duration="500" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="500" /> </set>  
 
 
要把上面定義的動畫效果應用的layout中,就要使用另外一個類:android.view.animation.LayoutAnimationController。該類把指定的效果應用到layout中的每個控件上去,使用layoutAnimation元素在xml文件中定義
  1. LayoutAnimationController,該文件同樣位於res/anim/目錄下,下面是一個示例(res\anim\layout_anim_ctrl.xml):   
  2. <layoutAnimation    
  3. xmlns:android="http://schemas.android.com/apk/res/android"  
  4. android:delay="30%"  
  5. android:animationOrder="reverse"  
  6. android:animation="@anim/translate_alpha_anim" />  
Java代碼  收藏代碼
  1. LayoutAnimationController,該文件同樣位於res/anim/目錄下,下面是一個示例(res\anim\layout_anim_ctrl.xml): <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:delay="30%" android:animationOrder="reverse" android:animation="@anim/translate_alpha_anim" />  
 
上面通過animation指定了使用哪個動畫(通過修改該值可以測試不同的動畫效果),animationOrder指定了通過逆序的方式應用動畫(在垂直的LinearLayout中就是從下往上逐個控件應用),delay指定了layout中的每個控動畫的延時時間爲動畫持續總時間的30%。
 
定義好LayoutAnimationController後就可以在Layout中使用了,這裏使用一個ListView做演示,佈局代碼如下:
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <LinearLayout    
  3. xmlns:android="http://schemas.android.com/apk/res/android"  
  4. android:orientation="vertical"    
  5. android:layout_width="fill_parent"  
  6. android:layout_height="fill_parent">   
  7.     <ListView    
  8.     android:id="@+id/list"    
  9.     android:layout_width="fill_parent"  
  10.     android:layout_height="fill_parent"    
  11.        
  12.     android:persistentDrawingCache="animation|scrolling"  
  13.     android:layoutAnimation="@anim/layout_anim_ctrl" />   
  14. </LinearLayout>  
Java代碼  收藏代碼
  1. <?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"> <ListView android:id="@+id/list" android:layout_width="fill_parent" android:layout_height="fill_parent" android:persistentDrawingCache="animation|scrolling" android:layoutAnimation="@anim/layout_anim_ctrl" /> </LinearLayout>  
 
 
上面的代碼中通過layoutAnimation指定了前面定義的LayoutAnimationController,爲了使動畫效果比較流暢這裏還通過persistentDrawingCache設置了控件的繪製緩存策略,一共有4中策略:
PERSISTENT_NO_CACHE 說明不在內存中保存繪圖緩存; 
PERSISTENT_ANIMATION_CACHE 說明只保存動畫繪圖緩存;
PERSISTENT_SCROLLING_CACHE 說明只保存滾動效果繪圖緩存
PERSISTENT_ALL_CACHES 說明所有的繪圖緩存都應該保存在內存中。
 
在Activity中並沒有什麼變化,代碼如下(src\org\goodev\animation\ListActivity.java):
  1. public class ListActivity extends Activity {   
  2.     
  3.     String[] mListItems =    
  4.     { "Item 1""Item 2""Item 3""Item 4""Item 5"};   
  5.     
  6.     @Override  
  7.     public void onCreate(Bundle savedInstanceState) {   
  8.        super.onCreate(savedInstanceState);   
  9.        setContentView(R.layout.list);   
  10.     
  11.        ArrayAdapter<String> listItemAdapter =    
  12.            new ArrayAdapter<String>(this,   
  13.               android.R.layout.simple_list_item_1, mListItems);   
  14.        ListView lv = (ListView) this.findViewById(R.id.list);   
  15.        lv.setAdapter(listItemAdapter);   
  16.     }   
  17. }  
Java代碼  收藏代碼
  1. public class ListActivity extends Activity { String[] mListItems = { "Item 1""Item 2""Item 3""Item 4""Item 5"}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.list); ArrayAdapter<String> listItemAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mListItems); ListView lv = (ListView) this.findViewById(R.id.list); lv.setAdapter(listItemAdapter); } }  
 
 
控件動畫介紹
其實控件動畫也是佈局動畫的一種,可以看做是自定義的動畫實現,佈局動畫在XML中定義OPhone已經實現的幾個動畫效果(AlphaAnimation、TranslateAnimation、ScaleAnimation、RotateAnimation)而控件動畫就是在代碼中繼承android.view.animation.Animation類來實現自定義效果。
 
控件動畫實現
通過重寫Animation的 applyTransformation (float interpolatedTime, Transformation t)函數來實現自定義動畫效果,另外一般也會實現 initialize (int width, int height, int parentWidth, int parentHeight)函數,這是一個回調函數告訴Animation目標View的大小參數,在這裏可以初始化一些相關的參數,例如設置動畫持續時間、設置Interpolator、設置動畫的參考點等。
OPhone在繪製動畫的過程中會反覆的調用applyTransformation 函數,每次調用參數interpolatedTime值都會變化,該參數從0漸變爲1,當該參數爲1時表明動畫結束。通過參數Transformation 來獲取變換的矩陣(matrix),通過改變矩陣就可以實現各種複雜的效果。關於矩陣的詳細信息可以參考android.graphics.Matrix的API文檔(http://androidappdocs-staging.appspot.com/reference/android/graphics/Matrix.html )。
 
下面來看一個簡單的實現:
   
  1. class ViewAnimation extends Animation {   
  2.        public ViewAnimation() {   
  3.        }   
  4.     
  5.        @Override  
  6.         public void initialize(int width, int height, int parentWidth, int parentHeight) {   
  7.            super.initialize(width, height, parentWidth, parentHeight);   
  8.            setDuration(2500);   
  9.            setFillAfter(true);   
  10.            setInterpolator(new LinearInterpolator());   
  11.        }   
  12.     
  13.        @Override  
  14.         protected void applyTransformation(float interpolatedTime,   
  15.               Transformation t) {   
  16.            final Matrix matrix = t.getMatrix();   
  17.            matrix.setScale(interpolatedTime, interpolatedTime);   
  18.               
  19.        }   
  20.     }  
Java代碼  收藏代碼
  1. class ViewAnimation extends Animation { public ViewAnimation() { } @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) { final Matrix matrix = t.getMatrix(); matrix.setScale(interpolatedTime, interpolatedTime); } }  
 
 
上面的代碼很簡單,在initialize函數中設置變換持續的時間2500毫秒,然後設置Interpolator爲LinearInterpolator並設置FillAfter爲true這樣可以在動畫結束的時候保持動畫的完整性。在applyTransformation函數中通過MatrixsetScale函數來縮放,該函數的兩個參數代表X、Y軸縮放因子,由於interpolatedTime是從0到1變化所在這裏實現的效果就是控件從最小逐漸變化到最大。調用View的startAnimation函數(參數爲Animation)就可以使用自定義的動畫了。代碼如下(src\org\goodev\animation\ViewAnimActivity.java):
 
  1. public class ViewAnimActivity extends Activity {   
  2.     
  3.     Button mPlayBtn;   
  4.     ImageView mAnimImage;   
  5.     
  6.     @Override  
  7.     public void onCreate(Bundle savedInstanceState) {   
  8.        super.onCreate(savedInstanceState);   
  9.        setContentView(R.layout.view_anim_layout);   
  10.        mAnimImage = (ImageView) this.findViewById(R.id.anim_image);   
  11.     
  12.        mPlayBtn = (Button) findViewById(R.id.play_btn);   
  13.        mPlayBtn.setOnClickListener(new OnClickListener() {   
  14.     
  15.            @Override  
  16.            public void onClick(View view) {   
  17.               mAnimImage.startAnimation(new ViewAnimation());   
  18.            }   
  19.     
  20.        });   
  21.     
  22.     }   
  23. }  
Java代碼  收藏代碼
  1. public class ViewAnimActivity extends Activity { Button mPlayBtn; ImageView mAnimImage; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.view_anim_layout); mAnimImage = (ImageView) this.findViewById(R.id.anim_image); mPlayBtn = (Button) findViewById(R.id.play_btn); mPlayBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { mAnimImage.startAnimation(new ViewAnimation()); } }); } }  
 
 
佈局代碼如下(res\layout\view_anim_layout.xml):
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <LinearLayout    
  3. xmlns:android="http://schemas.android.com/apk/res/android"  
  4. android:orientation="vertical"  
  5. android:layout_width="fill_parent"  
  6. android:layout_height="fill_parent"  
  7. >    
  8. <Button   
  9. android:id="@+id/play_btn"  
  10. android:layout_width="fill_parent"  
  11. android:layout_height="wrap_content"  
  12. android:text="Start Animation"  
  13. />   
  14. <ImageView    
  15. android:id="@+id/anim_image"  
  16. android:persistentDrawingCache="animation|scrolling"  
  17. android:layout_width="fill_parent"    
  18. android:layout_height="wrap_content"    
  19. android:src="@drawable/ophone"  
  20. />   
  21. </LinearLayout>  
Java代碼  收藏代碼
  1. <?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/play_btn" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Start Animation" /> <ImageView android:id="@+id/anim_image" android:persistentDrawingCache="animation|scrolling" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/ophone" /> </LinearLayout>  
 
從上圖可以看到ImageView是從左上角出來的,這是由於沒有指定矩陣的變換參考位置,默認位置爲(0,0),如果要想讓ImageView從中間出來,可以通過矩陣變換來把參考點移動到中間來,如下實現:
   
  1. class ViewAnimation extends Animation {   
  2.        int mCenterX;//記錄View的中間座標   
  3.        int mCenterY;   
  4.        public ViewAnimation() {   
  5.        }   
  6.     
  7.        @Override  
  8.        public void initialize(int width, int height, int parentWidth, int parentHeight) {   
  9.            super.initialize(width, height, parentWidth, parentHeight);   
  10.            //初始化中間座標值   
  11.            mCenterX = width/2;    
  12.            mCenterY = height/2;   
  13.            setDuration(2500);   
  14.            setFillAfter(true);   
  15.            setInterpolator(new LinearInterpolator());   
  16.        }   
  17.     
  18.        @Override  
  19.        protected void applyTransformation(float interpolatedTime,   
  20.               Transformation t) {   
  21.            final Matrix matrix = t.getMatrix();   
  22.            matrix.setScale(interpolatedTime, interpolatedTime);   
  23.            //通過座標變換,把參考點(0,0)移動到View中間   
  24.            matrix.preTranslate(-mCenterX, -mCenterY);   
  25.            //動畫完成後再移回來   
  26.            matrix.postTranslate(mCenterX, mCenterY);   
  27.        }   
  28.     }  
Java代碼  收藏代碼
  1. class ViewAnimation extends Animation { int mCenterX;//記錄View的中間座標 int mCenterY; public ViewAnimation() { } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); //初始化中間座標值 mCenterX = width/2; mCenterY = height/2; setDuration(2500); setFillAfter(true); setInterpolator(new LinearInterpolator()); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { final Matrix matrix = t.getMatrix(); matrix.setScale(interpolatedTime, interpolatedTime); //通過座標變換,把參考點(0,0)移動到View中間 matrix.preTranslate(-mCenterX, -mCenterY); //動畫完成後再移回來 matrix.postTranslate(mCenterX, mCenterY); } }  
 
 
preTranslate函數是在縮放前移動而postTranslate是在縮放完成後移動。現在ImageView就是從中間出來的了。這樣通過操作Matrix 可以實現各種複雜的變換。由於操作Matrix是實現動畫變換的重點,這裏簡單介紹下Matrix的常用操作:
  • Reset():重置該矩陣
  • setScale():設置矩陣縮放
  • setTranslate():設置矩陣移動
  • setRotate():設置矩陣旋轉
  • setSkew(): 使矩陣變形(扭曲)
 
矩陣也可以相乘,從線性代數中的矩陣運算中知道M1*M2 和M2*M1 是不一樣的,所以在使用concat(m1,m2)函數的時候要注意順序。
 
另外需要注意的是Matrix提供的API在OPhone1.0和OPhone1.5中是有變化的,請注意查看相關文檔。
 
OPhone還提供了一個用來監聽Animation事件的監聽接口AnimationListener,如果你對Animatioin何時開始、何時結束、何時重複播放感興趣則可以實現該接口。該接口提供了三個回調函數:onAnimationStart、onAnimationEnd、onAnimationRepeat。
 
使用Camera實現3D變換效果
最後來簡單介紹下OPhone提供的android.graphics.Camera類,通過該類可以在2D條件下實現3D動畫效果,該類可以看做一個視圖顯示的3D空間,然後可以在裏面做各種操作。把上面的ViewAnimation修改爲如下實現可以具體看看Camera的功能:
   
  1.  class ViewAnimation extends Animation {   
  2.        int mCenterX;//記錄View的中間座標   
  3.        int mCenterY;   
  4.        Camera camera = new Camera();   
  5.        public ViewAnimation() {   
  6.        }   
  7.     
  8.        @Override  
  9.        public void initialize(int width, int height, int parentWidth,   
  10.               int parentHeight) {   
  11.            super.initialize(width, height, parentWidth, parentHeight);   
  12.            //初始化中間座標值   
  13.            mCenterX = width/2;    
  14.            mCenterY = height/2;   
  15.            setDuration(2500);   
  16.            setFillAfter(true);   
  17.            setInterpolator(new LinearInterpolator());   
  18.        }   
  19.     
  20.        @Override  
  21.        protected void applyTransformation(float interpolatedTime,   
  22.               Transformation t) {   
  23. //         final Matrix matrix = t.getMatrix();   
  24. //         matrix.setScale(interpolatedTime, interpolatedTime);   
  25. //         //通過座標變換,把參考點(0,0)移動到View中間   
  26. //         matrix.preTranslate(-mCenterX, -mCenterY);   
  27. //         //動畫完成後再移回來   
  28. //         matrix.postTranslate(mCenterX, mCenterY);   
  29.            final Matrix matrix = t.getMatrix();   
  30.            camera.save();   
  31.            camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime));   
  32.            camera.rotateY(360 * interpolatedTime);   
  33.            camera.getMatrix(matrix);   
  34.            matrix.preTranslate(-mCenterX, -mCenterY);   
  35.            matrix.postTranslate(mCenterX, mCenterY);   
  36.            camera.restore();   
  37.        }   
  38.     }  
Java代碼  收藏代碼
  1. class ViewAnimation extends Animation { int mCenterX;//記錄View的中間座標 int mCenterY; Camera camera = new Camera(); public ViewAnimation() { } @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); //初始化中間座標值 mCenterX = width/2; mCenterY = height/2; setDuration(2500); setFillAfter(true); setInterpolator(new LinearInterpolator()); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { // final Matrix matrix = t.getMatrix(); // matrix.setScale(interpolatedTime, interpolatedTime); // //通過座標變換,把參考點(0,0)移動到View中間 // matrix.preTranslate(-mCenterX, -mCenterY); // //動畫完成後再移回來 // matrix.postTranslate(mCenterX, mCenterY); final Matrix matrix = t.getMatrix(); camera.save(); camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime)); camera.rotateY(360 * interpolatedTime); camera.getMatrix(matrix); matrix.preTranslate(-mCenterX, -mCenterY); matrix.postTranslate(mCenterX, mCenterY); camera.restore(); } }  
 
camera.translate(0.0f, 0.0f, (1300 - 1300.0f * interpolatedTime))在第一次調用的時候interpolatedTime值爲0,相當於把ImageView在Z軸後移1300像素,然後逐步的往前移動到0,同時camera.rotateY(360 * interpolatedTime)函數又把ImageView沿Y軸翻轉360度
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章