Android 滑動效果進階篇(五)—— 3D旋轉

前面介紹了利用Android自帶的控件,進行滑動翻頁製作效果,現在我們通過代碼實現一些滑動翻頁的動畫效果。

Animation實現動畫有兩個方式:幀動畫(frame-by-frame animation)和補間動畫(tweened animation


本示例通過繼承Animation自定義Rotate3D,實現3D翻頁效果。效果圖如下:



1、Rotate3D(Animation)

首先,自定義Animation的3D動畫類Rotate3D

  1. public class Rotate3D extends Animation {  
  2.     private float fromDegree;   // 旋轉起始角度   
  3.     private float toDegree;     // 旋轉終止角度   
  4.     private float mCenterX;     // 旋轉中心x   
  5.     private float mCenterY;     // 旋轉中心y   
  6.     private Camera mCamera;  
  7.   
  8.     public Rotate3D(float fromDegree, float toDegree, float centerX, float centerY) {  
  9.         this.fromDegree = fromDegree;  
  10.         this.toDegree = toDegree;  
  11.         this.mCenterX = centerX;  
  12.         this.mCenterY = centerY;  
  13.   
  14.     }  
  15.   
  16.     @Override  
  17.     public void initialize(int width, int height, int parentWidth, int parentHeight) {  
  18.         super.initialize(width, height, parentWidth, parentHeight);  
  19.         mCamera = new Camera();  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void applyTransformation(float interpolatedTime, Transformation t) {  
  24.         final float FromDegree = fromDegree;  
  25.         float degrees = FromDegree + (toDegree - fromDegree) * interpolatedTime;    // 旋轉角度(angle)   
  26.         final float centerX = mCenterX;  
  27.         final float centerY = mCenterY;  
  28.         final Matrix matrix = t.getMatrix();  
  29.   
  30.         if (degrees <= -76.0f) {  
  31.             degrees = -90.0f;  
  32.             mCamera.save();  
  33.             mCamera.rotateY(degrees);       // 旋轉   
  34.             mCamera.getMatrix(matrix);  
  35.             mCamera.restore();  
  36.         } else if (degrees >= 76.0f) {  
  37.             degrees = 90.0f;  
  38.             mCamera.save();  
  39.             mCamera.rotateY(degrees);  
  40.             mCamera.getMatrix(matrix);  
  41.             mCamera.restore();  
  42.         } else {  
  43.             mCamera.save();  
  44.             mCamera.translate(00, centerX);       // 位移x   
  45.             mCamera.rotateY(degrees);  
  46.             mCamera.translate(00, -centerX);  
  47.             mCamera.getMatrix(matrix);  
  48.             mCamera.restore();  
  49.         }  
  50.   
  51.         matrix.preTranslate(-centerX, -centerY);  
  52.         matrix.postTranslate(centerX, centerY);  
  53.     }  
  54. }  
public class Rotate3D extends Animation {
	private float fromDegree;	// 旋轉起始角度
	private float toDegree;		// 旋轉終止角度
	private float mCenterX;		// 旋轉中心x
	private float mCenterY;		// 旋轉中心y
	private Camera mCamera;

	public Rotate3D(float fromDegree, float toDegree, float centerX, float centerY) {
		this.fromDegree = fromDegree;
		this.toDegree = toDegree;
		this.mCenterX = centerX;
		this.mCenterY = centerY;

	}

	@Override
	public void initialize(int width, int height, int parentWidth, int parentHeight) {
		super.initialize(width, height, parentWidth, parentHeight);
		mCamera = new Camera();
	}

	@Override
	protected void applyTransformation(float interpolatedTime, Transformation t) {
		final float FromDegree = fromDegree;
		float degrees = FromDegree + (toDegree - fromDegree) * interpolatedTime;	// 旋轉角度(angle)
		final float centerX = mCenterX;
		final float centerY = mCenterY;
		final Matrix matrix = t.getMatrix();

		if (degrees <= -76.0f) {
			degrees = -90.0f;
			mCamera.save();
			mCamera.rotateY(degrees);		// 旋轉
			mCamera.getMatrix(matrix);
			mCamera.restore();
		} else if (degrees >= 76.0f) {
			degrees = 90.0f;
			mCamera.save();
			mCamera.rotateY(degrees);
			mCamera.getMatrix(matrix);
			mCamera.restore();
		} else {
			mCamera.save();
			mCamera.translate(0, 0, centerX);		// 位移x
			mCamera.rotateY(degrees);
			mCamera.translate(0, 0, -centerX);
			mCamera.getMatrix(matrix);
			mCamera.restore();
		}

		matrix.preTranslate(-centerX, -centerY);
		matrix.postTranslate(centerX, centerY);
	}
}

然後,實例化Rotate3D的旋轉方向

  1. public void initAnimation() {  
  2.     // 獲取旋轉中心   
  3.     DisplayMetrics dm = new DisplayMetrics();  
  4.     dm = getResources().getDisplayMetrics();  
  5.     mCenterX = dm.widthPixels / 2;  
  6.     mCenterY = dm.heightPixels / 2;  
  7.       
  8.     // 定義旋轉方向   
  9.     int duration = 1000;  
  10.     lQuest1Animation = new Rotate3D(0, -90, mCenterX, mCenterY);    // 下一頁的【question1】旋轉方向(從0度轉到-90,參考系爲水平方向爲0度)   
  11.     lQuest1Animation.setFillAfter(true);  
  12.     lQuest1Animation.setDuration(duration);  
  13.   
  14.     lQuest2Animation = new Rotate3D(900, mCenterX, mCenterY);     // 下一頁的【question2】旋轉方向(從90度轉到0,參考系爲水平方向爲0度)(起始第一題)   
  15.     lQuest2Animation.setFillAfter(true);  
  16.     lQuest2Animation.setDuration(duration);  
  17.   
  18.     rQuest1Animation = new Rotate3D(090, mCenterX, mCenterY);     // 上一頁的【question1】旋轉方向(從0度轉到90,參考系爲水平方向爲0度)   
  19.     rQuest1Animation.setFillAfter(true);  
  20.     rQuest1Animation.setDuration(duration);  
  21.   
  22.     rQuest2Animation = new Rotate3D(-900, mCenterX, mCenterY);    // 上一頁的【question2】旋轉方向(從-90度轉到0,參考系爲水平方向爲0度)   
  23.     rQuest2Animation.setFillAfter(true);  
  24.     rQuest2Animation.setDuration(duration);  
  25. }  
	public void initAnimation() {
		// 獲取旋轉中心
		DisplayMetrics dm = new DisplayMetrics();
		dm = getResources().getDisplayMetrics();
		mCenterX = dm.widthPixels / 2;
		mCenterY = dm.heightPixels / 2;
		
		// 定義旋轉方向
		int duration = 1000;
		lQuest1Animation = new Rotate3D(0, -90, mCenterX, mCenterY);	// 下一頁的【question1】旋轉方向(從0度轉到-90,參考系爲水平方向爲0度)
		lQuest1Animation.setFillAfter(true);
		lQuest1Animation.setDuration(duration);

		lQuest2Animation = new Rotate3D(90, 0, mCenterX, mCenterY);		// 下一頁的【question2】旋轉方向(從90度轉到0,參考系爲水平方向爲0度)(起始第一題)
		lQuest2Animation.setFillAfter(true);
		lQuest2Animation.setDuration(duration);

		rQuest1Animation = new Rotate3D(0, 90, mCenterX, mCenterY);		// 上一頁的【question1】旋轉方向(從0度轉到90,參考系爲水平方向爲0度)
		rQuest1Animation.setFillAfter(true);
		rQuest1Animation.setDuration(duration);

		rQuest2Animation = new Rotate3D(-90, 0, mCenterX, mCenterY);	// 上一頁的【question2】旋轉方向(從-90度轉到0,參考系爲水平方向爲0度)
		rQuest2Animation.setFillAfter(true);
		rQuest2Animation.setDuration(duration);
	}

2、Activity

首先,定義兩個佈局文件,用於旋轉的畫面切換

main.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/layout_main"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="wrap_content"              
  5.     android:orientation="vertical">  
  6.   
  7. ...  
  8.   
  9. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/layout_main"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"    		
	android:orientation="vertical">

...

</LinearLayout>

next.xml

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:id="@+id/layout_next"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="wrap_content"              
  5.     android:orientation="vertical">  
  6.   
  7. ...  
  8.   
  9. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/layout_next"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"    		
	android:orientation="vertical">

...

</LinearLayout>
限於篇幅,完整佈局文件請詳見源碼 ^_^


然後,初始化兩個旋轉的佈局文件資源

  1. private void initMain(){  
  2.        setContentView(R.layout.main);  
  3.   
  4.     layoutmain = (LinearLayout)findViewById(R.id.layout_main);  
  5.     btn_MainLast = (Button)findViewById(R.id.main_last);  
  6.     btn_MainNext = (Button)findViewById(R.id.main_next);  
  7.       
  8.     btn_MainLast.setOnClickListener(listener);  
  9.     btn_MainNext.setOnClickListener(listener);  
  10. }  
  11.   
  12. private void initNext(){  
  13.        setContentView(R.layout.next);  
  14.   
  15.     layoutnext = (LinearLayout)findViewById(R.id.layout_next);  
  16.     btn_NextLast = (Button)findViewById(R.id.next_last);  
  17.     btn_NextNext = (Button)findViewById(R.id.next_next);  
  18.       
  19.     btn_NextLast.setOnClickListener(listener);  
  20.     btn_NextNext.setOnClickListener(listener);  
  21. }  
	private void initMain(){
        setContentView(R.layout.main);

		layoutmain = (LinearLayout)findViewById(R.id.layout_main);
		btn_MainLast = (Button)findViewById(R.id.main_last);
		btn_MainNext = (Button)findViewById(R.id.main_next);
		
		btn_MainLast.setOnClickListener(listener);
		btn_MainNext.setOnClickListener(listener);
	}
	
	private void initNext(){
        setContentView(R.layout.next);

		layoutnext = (LinearLayout)findViewById(R.id.layout_next);
		btn_NextLast = (Button)findViewById(R.id.next_last);
		btn_NextNext = (Button)findViewById(R.id.next_next);
		
		btn_NextLast.setOnClickListener(listener);
		btn_NextNext.setOnClickListener(listener);
	}

最後,設置佈局文件中的按鈕監聽事件,響應3D旋轉動畫和方向

  1. private View.OnClickListener listener = new View.OnClickListener() {  
  2.     @Override  
  3.     public void onClick(View v) {  
  4.         switch (v.getId()) {  
  5.         case R.id.main_last:    // 上一頁   
  6.             layoutmain.startAnimation(lQuest1Animation);    // 當前頁向左旋轉(0,-90)   
  7.             initNext();  
  8.             layoutnext.startAnimation(lQuest2Animation);    // 下一頁向左旋轉(90, 0)   
  9.             break;  
  10.         case R.id.main_next:    // 下一頁   
  11.             layoutmain.startAnimation(rQuest1Animation);    // 當前頁向右旋轉(0,90)   
  12.             initNext();  
  13.             layoutnext.startAnimation(rQuest2Animation);    // 下一頁向右旋轉(-90, 0)   
  14.             break;  
  15.         case R.id.next_last:  
  16.             layoutnext.startAnimation(lQuest1Animation);  
  17.             initMain();  
  18.             layoutmain.startAnimation(lQuest2Animation);  
  19.             break;  
  20.         case R.id.next_next:  
  21.             layoutnext.startAnimation(rQuest1Animation);  
  22.             initMain();  
  23.             layoutmain.startAnimation(rQuest2Animation);  
  24.             break;  
  25.         }  
  26.     }  
  27. };  
	private View.OnClickListener listener = new View.OnClickListener() {
		@Override
		public void onClick(View v) {
			switch (v.getId()) {
			case R.id.main_last:	// 上一頁
				layoutmain.startAnimation(lQuest1Animation);	// 當前頁向左旋轉(0,-90)
				initNext();
				layoutnext.startAnimation(lQuest2Animation);	// 下一頁向左旋轉(90, 0)
				break;
			case R.id.main_next:	// 下一頁
				layoutmain.startAnimation(rQuest1Animation);	// 當前頁向右旋轉(0,90)
				initNext();
				layoutnext.startAnimation(rQuest2Animation);	// 下一頁向右旋轉(-90, 0)
				break;
			case R.id.next_last:
				layoutnext.startAnimation(lQuest1Animation);
				initMain();
				layoutmain.startAnimation(lQuest2Animation);
				break;
			case R.id.next_next:
				layoutnext.startAnimation(rQuest1Animation);
				initMain();
				layoutmain.startAnimation(rQuest2Animation);
				break;
			}
		}
	};


源碼下載




參考推薦:

animation


發佈了18 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章