这是我最近做的一个项目使用到的组件,具体效果如下:
这个组件我主要是通过graphics.Camera这个类实现的的景深,能够产生较好的3D翻页效果。
这是在尽量少使用Opengl的情况下的一种轻量级解决方案,这个组件的难点应该是在camera的位置变换以及手势操控上面,下面我把主要代码贴过来:
package com.hebin.test;import cm.hebin.test.R;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Camera;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Paint;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.animation.Animation;import android.view.animation.LinearInterpolator;import android.view.animation.Transformation;import android.widget.Toast;/** * @ClassName: HotGoodsView.java * @Description:热销品切换展示组件 * @Author: Hebin * @CreateDate: 2012/01/16 * @Version: v1.0.0 * */public class HotGoodsView extends View { private Camera mCamera = new Camera(); private Paint mPaint = new Paint(); private Matrix mMatrix = new Matrix(); /* * camera調用控制鏡頭位置 mCamera.translate(translate[0], translate[1], * translate[2]); */ private float[] mMiddleTranslate = new float[3]; private float[] mLeftTranslate = new float[3]; private float[] mRightBelowTranslate = new float[3]; private float[] mRightUpTranslate = new float[3]; // 初始化动画 private MyTransformAnimation mAnimation = new MyTransformAnimation(); private int images[] = { R.drawable.hot1, R.drawable.hot2, R.drawable.hot3, R.drawable.hot4, R.drawable.hot5 }; private String text[] = { "view1", "view2", "view3", "view4", "view5" }; // 主展示图坐标 private static final float mPositionX = CircleViewActivity.wWidth / 7 - 10; private static final float mPositionY = CircleViewActivity.wHeight / 15; // 主視圖Y軸旋轉角度 private static final float mRotateY = -10; // 主视图标记 private int flag = 0; // left_right :0初始化,1左滑 2右滑 private int left_right = 0; // 各視圖座標及旋轉角度參數 private float mLeftRotateY; private float mMiddleRotateY; private float mMiddlePositionX; private float mMiddlePositionY; private float mLeftPositionX; private float mLeftPositionY; // touch事件座標 private float transX; private float transY; // 左右滑动及复原动画標記 boolean doToLeftAnimation = false; boolean doReturnAnimation = false; boolean doToRightAnimation = false; // 左右滑动判断标记 private boolean moveToLeft = false; boolean click = false; private Context context; public HotGoodsView(Context context, AttributeSet attrs) { super(context, attrs); // 抗锯齿 mPaint.setAntiAlias(true); this.context = context; init(); } // 初始化、重置参数 private void init() { mMiddlePositionX = mPositionX; mMiddlePositionY = mPositionY; mMiddleRotateY = mRotateY; mLeftPositionX = -80 - mPositionX * 7 / 2; mLeftPositionY = mPositionY; mLeftRotateY = 0; transX = 0; transY = 0; mMiddleTranslate[0] = 0; mMiddleTranslate[1] = 0; mMiddleTranslate[2] = 0; mRightUpTranslate[0] = 30; mRightUpTranslate[1] = -10; mRightUpTranslate[2] = 50; mRightBelowTranslate[0] = 60; mRightBelowTranslate[1] = -20; mRightBelowTranslate[2] = 100; doToLeftAnimation = false; doToRightAnimation = false; doReturnAnimation = false; left_right = 0; System.gc(); } @Override protected void onDraw(Canvas canvas) { drawRight(canvas); drawMiddle(canvas); drawLeft(canvas); super.onDraw(canvas); } private void drawMiddle(Canvas canvas) { doDraw(mMiddleRotateY, mMiddleTranslate, canvas, mMiddlePositionX, mMiddlePositionY, images[flag]); } private void drawLeft(Canvas canvas) { Log.e("sss", mLeftRotateY + " " + mLeftTranslate + " " + mLeftPositionX + " " + mLeftPositionY + " " + (flag - 1)); if (flag != 0) doDraw(mLeftRotateY, mLeftTranslate, canvas, mLeftPositionX, mLeftPositionY, images[flag - 1]); } private void drawRight(Canvas canvas) { if ((flag != images.length - 2) && (flag != images.length - 1)) doDraw(mRotateY, mRightBelowTranslate, canvas, mPositionX, mPositionY, images[flag + 2]); if (flag != images.length - 1) doDraw(mRotateY, mRightUpTranslate, canvas, mPositionX, mPositionY, images[flag + 1]); } public int getFlag() { return flag; } public void setFlag(int flag) { this.flag = flag; } public String[] getText() { return text; } public void setText(String[] text) { this.text = text; } /** * * @MethodName:onTouchEvent * @Author: Hebin * @Description:触摸判断 * @CreateDate: 2012/01/16 */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: click = true; // 捕捉touch座標 transX = event.getX(); transY = event.getY(); // Log.e("xxx", transX + " " + transY); break; case MotionEvent.ACTION_MOVE: moveToLeft = ((event.getX() - transX) < 0); click = false; Log.e("xxxx", left_right + ""); // 判斷左右滑動 if (moveToLeft) { // 判斷左滑過程中是否曾經右滑(区分手指不離開屏幕左右滑動和其他情況) if (left_right != 2) { // left_right為0或1; if (mMiddlePositionX > -10 - mPositionX * 7 / 2) {// 判斷主視圖是否滑動到左側邊緣 // 主视图随手势移动 mMiddlePositionX += (event.getX() - transX); mLeftPositionX += (event.getX() - transX); // Log.e("xx", event.getX() + " " + transX + " " // + (event.getX() - transX)); // 主視圖旋轉 mMiddleRotateY -= ((event.getX() - transX) / 230) * 20; // Log.e("xx", mMiddleRotateY + ""); // 右側層疊視圖按軌道上升 mRightUpTranslate[0] -= ((transX - event.getX()) / 8); mRightUpTranslate[1] += ((transX - event.getX())) / 25; mRightUpTranslate[2] -= ((transX - event.getX())) * 5 / 23; // Log.e("xxx", mRightUpTranslate[0] + ""); // Log.e("xx", "ACTION_MOVE:" + mMiddlePositionX); mRightBelowTranslate[0] -= ((transX - event.getX())) / 8; mRightBelowTranslate[1] += ((transX - event.getX())) / 25; mRightBelowTranslate[2] -= ((transX - event.getX())) * 5 / 23; } left_right = 1; } else { // left_right=2;左滑过程中曾经右滑 if (mLeftPositionX > -80 - mPositionX * 7 / 2) { mLeftRotateY -= ((event.getX() - transX) / 230) * 10; mLeftPositionX += (event.getX() - transX); // Log.e("xx", mMiddleRotateY + ""); mMiddleTranslate[0] -= ((transX - event.getX()) / 8); mMiddleTranslate[1] += ((transX - event.getX())) / 25; mMiddleTranslate[2] -= ((transX - event.getX())) * 5 / 23; // Log.e("xxx", mRightUpTranslate[0] + ""); // Log.e("xx", "ACTION_MOVE:" + mMiddlePositionX); mRightUpTranslate[0] -= ((transX - event.getX())) / 8; mRightUpTranslate[1] += ((transX - event.getX())) / 25; mRightUpTranslate[2] -= ((transX - event.getX())) * 5 / 23; mRightBelowTranslate[0] -= ((transX - event.getX())) * 1 / 5; mRightBelowTranslate[1] += ((transX - event.getX())) / 23; mRightBelowTranslate[2] -= ((transX - event.getX())) * 10 / 23; } } } else if ((event.getX() - transX) != 0) {// 右滑 Log.e("xxxxxx", left_right + "else"); if (left_right != 1) {// left_right=0或2; if (mLeftPositionX < mPositionX) {// 判斷右滑邊界 mLeftRotateY -= ((event.getX() - transX) / 230) * 10; mLeftPositionX += (event.getX() - transX); // Log.e("xx", mMiddleRotateY + ""); mMiddleTranslate[0] -= ((transX - event.getX()) / 8); mMiddleTranslate[1] += ((transX - event.getX())) / 25; mMiddleTranslate[2] -= ((transX - event.getX())) * 5 / 23; // Log.e("xxx", mRightUpTranslate[0] + ""); // Log.e("xx", "ACTION_MOVE:" + mMiddlePositionX); mRightUpTranslate[0] -= ((transX - event.getX())) / 8; mRightUpTranslate[1] += ((transX - event.getX())) / 25; mRightUpTranslate[2] -= ((transX - event.getX())) * 5 / 23; mRightBelowTranslate[0] -= ((transX - event.getX())) * 1 / 5; mRightBelowTranslate[1] += ((transX - event.getX())) / 23; mRightBelowTranslate[2] -= ((transX - event.getX())) * 10 / 23; } left_right = 2; } else { if (mMiddlePositionX < mPositionX) { mMiddlePositionX += (event.getX() - transX); mLeftPositionX += (event.getX() - transX); // Log.e("xx", event.getX() + " " + transX + " " // + (event.getX() - transX)); mMiddleRotateY -= ((event.getX() - transX) / 230) * 20; // Log.e("xx", mMiddleRotateY + ""); mRightUpTranslate[0] -= ((transX - event.getX()) / 8); mRightUpTranslate[1] += ((transX - event.getX())) / 25; mRightUpTranslate[2] -= ((transX - event.getX())) * 5 / 23; // Log.e("xxx", mRightUpTranslate[0] + ""); // Log.e("xx", "ACTION_MOVE:" + mMiddlePositionX); mRightBelowTranslate[0] -= ((transX - event.getX())) / 10; mRightBelowTranslate[1] += ((transX - event.getX())) / 30; mRightBelowTranslate[2] -= ((transX - event.getX())) * 5 / 23; } } } invalidate(); // 滑动过程动态赋值 transX = event.getX(); transY = event.getY(); break; case MotionEvent.ACTION_UP: if (click) { Toast.makeText(context, text[flag], Toast.LENGTH_LONG).show(); } // touch结束开始动画 // Log.e("xxx", mMiddleRotateY + ""); if (moveToLeft) {// 左滑動畫 if ((mMiddlePositionX - mPositionX) < -CircleViewActivity.wWidth / 4) { Log.e("xxxx", "qqqqqqqqqq"); doToLeftAnimation = true; startAnimation(mAnimation); } else { doReturnAnimation = true; startAnimation(mAnimation); } } else {// 右滑動畫 if ((mLeftPositionX - (-80 - CircleViewActivity.wWidth / 2)) > CircleViewActivity.wWidth / 4 && flag > 0) { doToRightAnimation = true; startAnimation(mAnimation); } else { doReturnAnimation = true; startAnimation(mAnimation); } } break; default: break; } return true; } /** * * @MethodName:doDraw * @Author: Hebin * @Description:实际绘图类 * @CreateDate: 2012/01/16 */ private void doDraw(float rotateY, float[] translate, Canvas canvas, float x, float y, int image) { canvas.save(); mCamera.save(); mCamera.rotateY(rotateY); mCamera.translate(translate[0], translate[1], translate[2]); mCamera.getMatrix(mMatrix); mCamera.applyToCanvas(canvas); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), image); canvas.drawBitmap(bitmap, x, y, mPaint); bitmap.recycle(); mCamera.restore(); canvas.restore(); } /** * @ClassName: MyTransformAnimation.java * @Description:切換動畫 * @Author: Hebin * @CreateDate: 2012/01/16 * @Version: v1.0.0 * @parameters: doToLeftAnimation = false; doReturnAnimation = false; * doToRightAnimation = false; */ class MyTransformAnimation extends Animation { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if (doToLeftAnimation) {// 左滑动画 mMiddlePositionX -= (mMiddlePositionX + 80 + mPositionX * 7 / 2) * interpolatedTime; mMiddleRotateY += (0 - mMiddleRotateY) * interpolatedTime; mRightUpTranslate[0] += (0 - mRightUpTranslate[0]) * interpolatedTime; mRightUpTranslate[1] += (0 - mRightUpTranslate[1]) * interpolatedTime; mRightUpTranslate[2] += (0 - mRightUpTranslate[2]) * interpolatedTime; mRightBelowTranslate[0] += (30 - mRightBelowTranslate[0]) * interpolatedTime; mRightBelowTranslate[1] += (-10 - mRightBelowTranslate[1]) * interpolatedTime; mRightBelowTranslate[2] += (50 - mRightBelowTranslate[2]) * interpolatedTime; // Log.e("", mRightUpTranslate[0] + ""); } if (doReturnAnimation) {// 復原動畫 mMiddlePositionX -= (mMiddlePositionX - mPositionX) * interpolatedTime; mMiddleRotateY -= (mMiddleRotateY + 15) * interpolatedTime; mLeftPositionX -= (mLeftPositionX + 80 + mPositionX * 7 / 2) * interpolatedTime; mLeftRotateY += (0 - mLeftRotateY) * interpolatedTime; // Log.e("xx", "doReturnAnimation:" + mMiddlePositionX); } if (doToRightAnimation) {// 右滑动画 // Log.e("ee", "doToRightAnimation"); mLeftPositionX -= (mLeftPositionX - mPositionX) * interpolatedTime; mLeftRotateY += (-10 - mLeftRotateY) * interpolatedTime; // Log.e("xxx", mLeftPositionX + ""); // Log.e("xx", mPositionX + ""); mMiddleTranslate[0] += (30 - mMiddleTranslate[0]) * interpolatedTime; mMiddleTranslate[1] += (-10 - mMiddleTranslate[1]) * interpolatedTime; mMiddleTranslate[2] += (50 - mMiddleTranslate[2]) * interpolatedTime; mRightUpTranslate[0] += (60 - mRightUpTranslate[0]) * interpolatedTime; mRightUpTranslate[1] += (-20 - mRightUpTranslate[1]) * interpolatedTime; mRightUpTranslate[2] += (100 - mRightUpTranslate[2]) * interpolatedTime; mRightBelowTranslate[0] += (120 - mRightBelowTranslate[0]) * interpolatedTime; mRightBelowTranslate[1] += (-40 - mRightBelowTranslate[1]) * interpolatedTime; mRightBelowTranslate[2] += (200 - mRightBelowTranslate[2]) * interpolatedTime; } super.applyTransformation(interpolatedTime, t); } // 動畫初始化 @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { // Log.e("xx", "initialize"); super.initialize(width, height, parentWidth, parentHeight); setDuration(250); setFillAfter(true); setInterpolator(new LinearInterpolator()); } } @Override protected void onAnimationEnd() { // 动画结束更新flag标记 if (doToLeftAnimation) { if (flag < images.length - 1) flag++; } if (doToRightAnimation) { if (flag > 0) flag--; } // 所有动态参数在动画结束后重置 init(); Log.e("xxxx", left_right + "onAnimationEnd"); super.onAnimationEnd(); } @Override protected void onAnimationStart() { super.onAnimationStart(); }}
我注释写的很详细,就不一一再解释什么。这部分代码写的比较原始,没做太多优化。