動畫效果可以大大提高界面的交互效果,因此,動畫在移動開發中的應用場景較為普遍。掌握基本的動畫效果在成熟的軟體開發中不可或缺。除此之外,用戶對於動畫的接受程度遠高於文字和圖片,利用動畫效果可以加深用戶對於產品的印象。因此本文給出安卓設計中幾種常見的動畫效果。 基礎知識 在介紹安卓中的動畫效果之前,有必 ...
動畫效果可以大大提高界面的交互效果,因此,動畫在移動開發中的應用場景較為普遍。掌握基本的動畫效果在成熟的軟體開發中不可或缺。除此之外,用戶對於動畫的接受程度遠高於文字和圖片,利用動畫效果可以加深用戶對於產品的印象。因此本文給出安卓設計中幾種常見的動畫效果。
基礎知識
在介紹安卓中的動畫效果之前,有必要介紹一下安卓中的圖片處理機制。圖片的特效包括圖形的縮放、鏡面、倒影、旋轉、平移等。圖片的特效處理方式是將原圖的圖形矩陣乘以一個特效矩陣,形成一個新的圖形矩陣來實現的。矩陣Matrix 類,維護了一個3*3 的矩陣去更改像素點的坐標。Android 手機的屏幕坐標系以左上角為原點,從左向右為x軸正方向,從上到下為y軸正方向。第一行表示像素點的x 坐標:x = 1*x + 0*y + 0*z,第二行表示像素點的y 坐標:y = 0*x + 1*y + 0*z,第三行表示像素點的z 坐標:z = 0*x + 0*y + 1*z。圖片的特效處理正是通過更改圖形矩陣的值來實現的,在android下Matrix這個類幫我們封裝了矩陣的一些基本用法,所以我們可以直接使用即可。用代碼編輯圖片,最好處理都是圖片在記憶體中的拷貝,不去處理原圖,因此需要用Bitmap創建一個與原圖大小一致,格式相同的空白點陣圖。
對照片進行操作的基本步驟:
1. 創建一個空白的bitmap,寬高信息和原圖保存一致;
2. 創建一個畫板;
3. 創建一個畫筆;
4. 設置matrix矩陣;
5. 對照著原圖在畫紙上面畫出一模一樣的樣子出來。
下麵分別給出對圖片縮放、平移、旋轉、鏡面操作的代碼。
/** * 圖片縮放 * */ private void zoom() { Bitmap srcBitmap = BitmapFactory.decodeFile("mnt/sdcard/b.jpg"); iv_src.setImageBitmap(srcBitmap); Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig()); Canvas canvas = new Canvas(copyBitmap); Paint paint = new Paint(); paint.setColor(Color.BLACK); Matrix matrix = new Matrix(); matrix.setScale(0.6f, 0.6f); canvas.drawBitmap(srcBitmap, matrix, paint); iv_dest.setImageBitmap(copyBitmap); } /** * 圖片平移 * */ public void translation(){ Options ops = new Options(); ops.inSampleSize = 4; //等比放縮 Bitmap srcBitmap = BitmapFactory.decodeFile("/mnt/sdcard/b.jpg",ops); iv_src.setImageBitmap(srcBitmap); Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig()); Canvas canvas = new Canvas(copyBitmap); Paint paint = new Paint(); paint.setColor(Color.BLACK); Matrix matrix = new Matrix(); matrix.setTranslate(100, 100); canvas.drawBitmap(srcBitmap, matrix, paint); iv_dest.setImageBitmap(copyBitmap); } /** * 旋轉 * */ public void scole(){ Bitmap srcBitmap = BitmapFactory.decodeFile("/mnt/sdcard/b.jpg"); iv_src.setImageBitmap(srcBitmap); Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig()); Canvas canvas = new Canvas(copyBitmap); Paint paint = new Paint(); paint.setColor(Color.BLACK); Matrix matrix = new Matrix(); matrix.setRotate(180, srcBitmap.getWidth()/2, srcBitmap.getHeight()/2);//繞原點旋轉 canvas.drawBitmap(srcBitmap, matrix, paint); iv_dest.setImageBitmap(copyBitmap); } /** * 鏡面特效/倒影特效 * 原理一樣,一個關於x軸旋轉,一個關於y軸旋轉 */ public void mirror(){ Bitmap srcBitmap = BitmapFactory.decodeFile("/mnt/sdcard/b.jpg"); iv_src.setImageBitmap(srcBitmap); Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig()); Canvas canvas = new Canvas(copyBitmap); Paint paint = new Paint(); paint.setColor(Color.BLACK); Matrix matrix = new Matrix(); matrix.setScale(-1, 1); matrix.postTranslate(srcBitmap.getWidth(), 0); canvas.drawBitmap(srcBitmap, matrix, paint); iv_dest.setImageBitmap(copyBitmap); }
接下來進入進入今天的主題。安卓下的動畫分為三種: 幀動畫、View動畫(補間動畫)、屬性動畫。下麵分別介紹這三種動畫。
幀動畫:
幀動畫是這三種動畫中最為普遍的一種,指的是一幀一幀播放的動畫。更直白的講就是快速切換圖片的效果。通過animation-list來實現,創建一個Drawable 序列,這些Drawable 可以按照指定的時間間隔一個一個的顯示,也就是順序播放事先做好的圖像。
幀動畫使用的基本步驟:
1 . 創建幀動畫每幀需要的圖片, 放到對應的 drawable-xxx 或drawable 目錄中
2 . 在drawable 目錄下,創建幀動畫 xml 文件,根節點選擇 animation-list。oneshot屬性表示幀動畫的自動執行。 如果為true,表示動畫只播放一次停止在最後一幀上,如果設置為false表示動畫迴圈播放。
3. 在JAVA代碼中開啟動畫。設置為 View 的 Background 或者 ImageView 的 src,然後獲取到控制項的 AnimationDrawable 對象,通過 AnimationDrawable.start() 方法啟動動畫
下麵以火箭的發射為例演示幀動畫。
xml文件中的代碼:
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" > <item android:drawable="@drawable/desktop_rocket_launch_1" android:duration="200" /> <item android:drawable="@drawable/desktop_rocket_launch_2" android:duration="200" /> </animation-list>
Java代碼的實現:
iv = new ImageView(this); //開啟幀動畫 rocket為上述xml文件 iv.setBackgroundResource(R.drawable.rocket); AnimationDrawable ad = (AnimationDrawable) iv.getBackground(); ad.start();
屬性動畫:
Android 3.0之後view類增加新的用來記錄動畫行為的屬性,只能運行的Android3.0以上的系統也就是說 運行在 API-11 以上的系統。屬性動畫會改變當前的視圖所在的位置,通過動態改變控制項的寬、高、坐標等屬性而產生的動畫效果。
通過控制控制項的屬性實現動畫效果,屬性動畫比補間動畫要更靈活、強大的多。需要註意的是屬性動畫內部其實並沒有區分位移、縮放、透明、旋轉等動畫,其核心思想只是修改一個控制項的屬性,我們可以通過修改不同的屬性來實現補間動畫的4 種效果而已。
屬性動畫跟補間動畫比,屬性動畫是真正改變了控制項的屬性,會改變當前的視圖所在的位置,因此當控制項的位置改變後只要點擊到了控制項“身上”就能觸發onClick 事件。而補間動畫則並沒用改變控制項的真實屬性,因此不管屬性動畫執行後將控制項移動到了哪個位置,只能通過點擊該控制項的原始位置才能觸發onClick 事件。
通過xml 文件實現屬性動畫步驟:
1. 在res 下創建屬性動畫文件。在res 目錄下創建animator 文件夾,然後創建一個objectAnimator 資源文件。資源名稱自定義即可。
2. 編寫屬性動畫文件。指定屬性值。
3. 編寫代碼使用屬性動畫文件。通過AnimatorInflater載入圖片資源,指定要顯示動畫的控制項,並開啟動畫。
屬性動畫可以通過xml文件實現,但通常屬性動畫是通過JAVA代碼實現。這裡僅給出用xml文件實現淡化動畫的案例,其他案例均以JAVA代碼的方式實現。
JAVA代碼的方式實現屬性動畫。
public class MainActivity extends Activity { private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); } /** * 淡化動畫 * @param view */ public void alpha(View view) { ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "alpha", new float[] { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f }); oa.setDuration(3000); oa.setRepeatCount(ObjectAnimator.INFINITE); oa.setRepeatMode(ObjectAnimator.REVERSE); oa.start(); } /** * 平移動畫 * @param view */ public void trans(View view) { ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", new float[] { 10f, 20f, 30f, 40f, 60f, 80f }); oa.setDuration(3000); oa.setRepeatCount(ObjectAnimator.INFINITE); oa.setRepeatMode(ObjectAnimator.REVERSE); oa.start(); } /** * 縮放動畫 */ public void scale(View view) { ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "scaleX", new float[] { 1f, 2f, 3f, 4f, 5f, 6f }); oa.setDuration(3000); oa.setRepeatCount(ObjectAnimator.INFINITE); oa.setRepeatMode(ObjectAnimator.REVERSE); oa.start(); } /** * 旋轉動畫 */ public void rotate(View view) { ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "rotationY", new float[] { 90f, 180f, 270f, 360f }); oa.setDuration(3000); oa.setRepeatCount(ObjectAnimator.INFINITE); oa.setRepeatMode(ObjectAnimator.REVERSE); oa.start(); } /** * 水平平移 + 豎直平移 */ public void set(View view) { AnimatorSet set = new AnimatorSet(); ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", new float[] { 10f, 20f, 30f, 40f, 60f, 80f }); oa.setDuration(3000); ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "translationY", new float[] { -10f, -20f, -30f, -40f, -60f, -80f }); oa2.setDuration(3000); set.playTogether(oa, oa2); set.start(); } }
xml文件實現淡化效果:
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="3000" android:propertyName="alpha" android:repeatCount="3" android:repeatMode="reverse" android:valueFrom="0.0" android:valueTo="1.0" > </objectAnimator>
然後在java代碼中實現這樣一段代碼:
public void alpha(View view) { Animator animator = AnimatorInflater.loadAnimator(this, R.animator.alpha); animator.setTarget(iv); animator.start(); }
View動畫:
漸變動畫也叫補間動畫。補間動畫通過對View 的內容進行一系列的圖形變換(包括平移、縮放、旋轉、改變透明度)來實現動畫效果。動畫效果的定義可以採用XML 文件來做也可以採用java 代碼來做。
使用XML 文件實現View動畫的步驟:
1. 在res 目錄下創建anim 文件夾。
2. 在anim 文件夾中創建xml文件,文件名可以自定義。
3. 編輯xml文件。定義不同的標簽,表示不同的動畫效果。alpha表示淡化,
4. 添加Java 邏輯代碼,使用AnimationUtils 工具類載入xml 文件,獲取Animation 對象,調用startAnimation 讓ImageView 執行此動畫。
View動畫中常用屬性的含義:
duration 動畫時長
fromAlpha 起始透明度,1 為完全不透明,0 為完全透明
repeatCount 重覆次數,INFINITE表示無限重覆
toAlpha 目標透明度
repeatMode 重覆模式,restart 為重新開始,reverse表示來回播放
漸變動畫在代碼中使用的是AlphaAnimation 類來定義,在XML 文件中使用<alpha>節點來定義。旋轉動畫在代碼中使用的是RotateAnimation 類來定義,在XML 文件中使用<rotate>節點來定義。伸縮動畫在代碼中使用的是ScaleAnimation 類來定義,在XML 文件中使用<scale>節點來定義。平移動畫在代碼中使用的是TranslateAnimation 類來定義,在XML 文件中使用<scale>節點來定義。Android 提供了AnimationSet 動畫集合用於將多種補間動畫聯合起來一起使用,這樣就能實現更多複雜的動畫效果。動畫集合既可以使用AnimationSet 類來定義也可以在XML 文件中使用<set>節點來定義。任何複雜的動畫均是通過簡單的動畫集合在一起的。
使用編碼方式同樣可以實現view動畫,直接創建相應的動畫對象,然後添加相應的屬。代使用編碼方式實現view動畫跟用XML 文件實現View動畫其實是一模一樣的,無非就是在JAVA代碼中設定相關的屬性罷了。
使用XML 文件實現View動畫的代碼:
1 . 在anim文件夾下新建下列文件:
<!-- alpha_demo.xml 文件夾下 --> <?xml version="1.0" encoding="utf-8"?> <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromAlpha="0.0" android:repeatCount="infinite" android:repeatMode="reverse" android:toAlpha="1.0" > </alpha> <!-- rotate_demo.xml 文件夾下 --> <?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:repeatCount="2" android:repeatMode="reverse" android:toDegrees="360" > </rotate> <!-- scale_demo.xml 文件夾下 --> <?xml version="1.0" encoding="utf-8"?> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromXScale="20%" android:fromYScale="20%" android:pivotX="50%" android:pivotY="50%" android:repeatCount="2" android:repeatMode="reverse" android:toXScale="200%" android:toYScale="200%" > </scale> <!-- set_demo.xml 文件夾下 --> <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true" > <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromDegrees="0" android:pivotX="50%" android:pivotY="50%" android:repeatCount="2" android:repeatMode="reverse" android:toDegrees="360" > </rotate> <scale xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromXScale="20%" android:fromYScale="20%" android:pivotX="50%" android:pivotY="50%" android:repeatCount="2" android:repeatMode="reverse" android:toXScale="200%" android:toYScale="200%" > </scale> </set> <!-- trans_demo.xml 文件夾下 --> <?xml version="1.0" encoding="utf-8"?> <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="2000" android:fromXDelta="0" android:fromYDelta="0" android:repeatCount="2" android:repeatMode="reverse" android:toXDelta="100%" android:toYDelta="100%" > </translate>
2 . 在java代碼中實現下列邏輯:
import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.ImageView; public class MainActivity extends Activity { private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); } /** * 淡化動畫 * @param view */ public void alpha(View view){ Animation aa = AnimationUtils.loadAnimation(this, R.anim.alpha_demo); iv.startAnimation(aa); } /** * 平移動畫 * @param view */ public void trans(View view){ Animation ta = AnimationUtils.loadAnimation(this, R.anim.trans_demo); iv.startAnimation(ta); } /** * 縮放動畫 */ public void scale(View view){ Animation sa = AnimationUtils.loadAnimation(this, R.anim.scale_demo); iv.startAnimation(sa); } /** * 旋轉動畫 */ public void rotate(View view){ Animation ra = AnimationUtils.loadAnimation(this, R.anim.rotate_demo); iv.startAnimation(ra); } /** * 旋轉 + 放縮 */ public void set(View view){ Animation set = AnimationUtils.loadAnimation(this, R.anim.set_demo); iv.startAnimation(set); } }
使用java代碼實現View動畫的代碼
import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import android.view.animation.RotateAnimation; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import android.widget.ImageView; public class MainActivity extends Activity { private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); } /** * 漸變動畫 * @param view */ public void alpha(View view) { AlphaAnimation aa = new AlphaAnimation(0.0f, 1.0f); aa.setDuration(2000); aa.setRepeatCount(Animation.INFINITE); aa.setRepeatMode(Animation.REVERSE); iv.startAnimation(aa); } /** * 平移動畫 * @param view */ public void trans(View view) { TranslateAnimation ta = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1f); ta.setDuration(2000); ta.setRepeatCount(Animation.INFINITE); ta.setRepeatMode(Animation.REVERSE); iv.startAnimation(ta); } /** * 縮放動畫 */ public void scale(View view) { ScaleAnimation sa = new ScaleAnimation(0.2f, 2.0f, 0.2f, 2.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); sa.setDuration(2000); sa.setRepeatCount(Animation.INFINITE); sa.setRepeatMode(Animation.REVERSE); iv.startAnimation(sa); } /** * 旋轉動畫 */ public void rotate(View view) { RotateAnimation ra = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); ra.setDuration(2000); ra.setRepeatCount(Animation.INFINITE); ra.setRepeatMode(Animation.REVERSE); iv.startAnimation(ra); } /** * 旋轉 + 平移 + 放縮 * AnimationSet添加各個動畫 */ public void set(View view) { AnimationSet set = new AnimationSet(false); RotateAnimation ra = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); ra.setDuration(2000); ra.setRepeatCount(Animation.INFINITE); ra.setRepeatMode(Animation.REVERSE); ScaleAnimation sa = new ScaleAnimation(0.2f, 2.0f, 0.2f, 2.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); sa.setDuration(2000); sa.setRepeatCount(Animation.INFINITE); sa.setRepeatMode(Animation.REVERSE); TranslateAnimation ta = new TranslateAnimation( Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1f); ta.setDuration(2000); ta.setRepeatCount(Animation.INFINITE); ta.setRepeatMode(Animation.REVERSE); set.addAnimation(ta); set.addAnimation(sa); set.addAnimation(ra); iv.startAnimation(set); } }
自此Android下的三種動畫全部講解完畢。