最近做了一個簡單的3D效果翻頁特效,先說說我的思路吧,首先我這個翻頁效果並不是兩個Activity之間的跳轉,而是在同一個activity類切換不同的view而已。我現在的做法是單擊一個button然後Gone當前的布局,然後把需要呈現的布局visible,在隱藏當前布局的時候啟動動畫,然後給動畫添加監聽,在動畫結束時開始另外一個view的入場動畫就行了。
下面來看下我的首頁面的布局檔案:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="fill_parent" android:layout_height="fill_parent" > <include android:layout_width="fill_parent" android:layout_height="fill_parent" layout="@layout/layout2" /> <include android:layout_width="fill_parent" android:layout_height="fill_parent" layout="@layout/layout1" /></FrameLayout>
我這個布局檔案使用<include>標籤包含了另外2個布局檔案,這些布局檔案才是呈現資料的,下面是另外2個布局檔案:
layout1:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/test1" android:orientation="vertical" android:id="@+id/container1" > <Button android:id="@+id/bt_towhile" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="白色" /></LinearLayout>
layout2:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container2" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/test2" android:orientation="vertical" > <Button android:id="@+id/bt_toblack" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="黑色" /></LinearLayout>
我這裡只是舉個例子並沒有放什麼實際的類容,只是放了2個button,當我點擊其中一個跳轉到另外一個layout。
有了布局檔案那我們就開始要實現功能了,我們的想法是點擊按鈕的時候開始一個動畫等動畫結束時再開啟另外一個動畫並隱藏和展示layout1和layout2。
下面是我寫的一個動畫工具類源碼:
package com.test.view;import android.view.View;import android.view.ViewGroup;import android.view.animation.Animation;import android.view.animation.DecelerateInterpolator;public class RotateAnimationUtil {private ViewGroup context;private View[] views;public RotateAnimationUtil(ViewGroup context, View... views) {super();this.context = context;this.views = views;}/** * 應用自訂的Rotate3DAnimation動畫 * * @param flag * 當前控制項的順序座標 * @param startDegress * 開始的角度 * @param endDegress * 結束的角度 */public void applyRotateAnimation(int flag, float startDegress,float endDegress) {final float centerX = context.getWidth() / 2.0f;final float centerY = context.getHeight() / 2.0f;Rotate3DAnimation rotate = new Rotate3DAnimation(startDegress,endDegress, centerX, centerY, 310.0f, true);rotate.setDuration(1000);rotate.setFillAfter(false);rotate.setInterpolator(new DecelerateInterpolator());rotate.setAnimationListener(new DisplayNextView(flag));context.startAnimation(rotate);}private final class DisplayNextView implements Animation.AnimationListener {private final int mFlag;private DisplayNextView(int flag) {mFlag = flag;}public void onAnimationStart(Animation animation) {}// 動畫結束public void onAnimationEnd(Animation animation) {context.post(new SwapViews(mFlag));}public void onAnimationRepeat(Animation animation) {}}/** * 新開一個線程動畫結束後再開始一次動畫效果實現翻屏特效 * * @author yangzhiqiang * */private final class SwapViews implements Runnable {private final int mFlag;public SwapViews(int mFlag) {this.mFlag = mFlag;}public void run() {final float centerX = context.getWidth() / 2.0f;final float centerY = context.getHeight() / 2.0f;Rotate3DAnimation rotation;if (mFlag > -1) {views[0].setVisibility(View.GONE);views[1].setVisibility(View.VISIBLE);views[1].requestFocus();rotation = new Rotate3DAnimation(270, 360, centerX, centerY,310.0f, false);} else {views[1].setVisibility(View.GONE);views[0].setVisibility(View.VISIBLE);views[0].requestFocus();rotation = new Rotate3DAnimation(90, 0, centerX, centerY,310.0f, false);}rotation.setDuration(1000);rotation.setFillAfter(false);rotation.setInterpolator(new DecelerateInterpolator());// 開始動畫context.startAnimation(rotation);}}}
我解釋一下這個類的構造方法:
public RotateAnimationUtil(ViewGroup context, View... views) {super();this.context = context;this.views = views;}
有2個參數,第一個參數就是我們的主布局頁面的FrameLayout,第2個參數就是我們要進行動畫切換的2個子layout,我這使用的是一個可變長參數只是為了方便而已。
public void applyRotateAnimation(int flag, float startDegress,float endDegress)方法第一個參數是標記當前是第是從哪個個layout跳轉,因外我們必須知道當前開始跳轉的layout才能推算角度。
下面是我自訂動畫的源碼:
package com.test.view;import android.graphics.Camera;import android.graphics.Matrix;import android.view.animation.Animation;import android.view.animation.Transformation;public class Rotate3DAnimation extends Animation {private final float mFormDegress;private final float mToDegress;private final float mCenterX;private final float mCenterY;/** * 控制z軸的一個常量值,主要是控制動畫的升降距離 */private final float mDepthz;/** * 控制z軸是像上移動還是向下移動,從而實現升降效果 */private final boolean mReverse;private Camera mCamera;public Rotate3DAnimation(float formDegress, float toDegress, float centerX,float centerY, float depthz, boolean reverse) {super();this.mFormDegress = formDegress;this.mToDegress = toDegress;this.mCenterX = centerX;this.mCenterY = centerY;this.mDepthz = depthz;this.mReverse = reverse;}@Overridepublic void initialize(int width, int height, int parentWidth,int parentHeight) {super.initialize(width, height, parentWidth, parentHeight);mCamera = new Camera();}/** * interpolatedTime 取值範圍是0-1之間當每次,當動畫啟動後會系統會不停的調用applyTransformation方法, * 並改變interpolatedTime的值 */@Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) {final float formDegress = mFormDegress;// 通過差點值計算出轉變的角度float degress = formDegress+ ((mToDegress - formDegress) * interpolatedTime);final float centerX = mCenterX;final float centerY = mCenterY;final Camera camera = mCamera;// 得到當前矩陣Matrix matrix = t.getMatrix();// 報錯當前螢幕的狀態camera.save();// 判斷是降還是升if (mReverse) {// 正向改變Z軸角度camera.translate(0.0f, 0.0f, mDepthz * interpolatedTime);} else {// 反向改變Z軸角度camera.translate(0.0f, 0.0f, mDepthz * (1.0f - interpolatedTime));}// 旋轉Y軸角度camera.rotateY(degress);// 把當前改變後的矩陣資訊複製給Transformation的Matrixcamera.getMatrix(matrix);// 根據改變後的矩陣資訊從新恢複螢幕camera.restore();// 讓動畫在螢幕中間運行matrix.preTranslate(-centerX, -centerY);matrix.postTranslate(centerX, centerY);}}
如果你不需要沉降效果那麼你把下面的代碼刪除掉即可:
if (mReverse) {// 正向改變Z軸角度camera.translate(0.0f, 0.0f, mDepthz * interpolatedTime);} else {// 反向改變Z軸角度camera.translate(0.0f, 0.0f, mDepthz * (1.0f - interpolatedTime));}
好了核心代碼已經上完,下面是主介面代碼:
package com.test.rotateanimation;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.View;import android.view.ViewGroup;import android.widget.Button;import android.widget.FrameLayout;import android.widget.LinearLayout;import com.test.view.RotateAnimationUtil;public class MainActivity extends Activity {private FrameLayout container;private LinearLayout container1;private LinearLayout container2;private RotateAnimationUtil rotateAnimationUtil;private Button bt_towhile;private Button bt_toblack;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();bt_towhile.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {rotateAnimationUtil.applyRotateAnimation(1, 0, 90);}});bt_toblack.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {rotateAnimationUtil.applyRotateAnimation(-1, 0, -90);}});// 設定當前View控制項的緩衝container.setPersistentDrawingCache(ViewGroup.PERSISTENT_ANIMATION_CACHE);}private void initView() {container = (FrameLayout) findViewById(R.id.container);bt_toblack = (Button) findViewById(R.id.bt_toblack);bt_towhile = (Button) findViewById(R.id.bt_towhile);container1 = (LinearLayout) findViewById(R.id.container1);container2 = (LinearLayout) findViewById(R.id.container2);rotateAnimationUtil = new RotateAnimationUtil(container, container1,container2);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.activity_main, menu);return true;}}
下面是運行效果,剪下效果不好,呵呵.
源碼:http://download.csdn.net/detail/yaoyeyzq/4892748