转载请注明来自: 5进制空间-android区
相信做电子书的同学,都遇到过翻页动画的需求吧,如果你不满足与点击滑动翻页的话,这边文章应该能够帮助到你。
先上个效果图:
效果还是很不错的,不过与ibook那个效果比起来,还是有差距的。应为这个没用到openGL做3D效果,只是用的2d的canvas画布去画的view,添加了阴影效果,还是挺有立体感的。而且比较流畅。openGL实现肯定效果会更好,不过就我目前的技术实力,实现希望还是渺茫的。
废话少说,还是上代码吧:
这里需要两个UI的view类和一个使用方法的demo。
第一个pageTurnerView.java:
public class PageTurnerViewP1 extends RelativeLayout{ private static final int CORNER_RIGHT_MASK = 1; private static final int CORNER_TOP_MASK = 2; public static final int CORNER_BOTTOM_LEFT = 0; public static final int CORNER_BOTTOM_RIGHT = 1; public static final int CORNER_TOP_LEFT = 2; public static final int CORNER_TOP_RIGHT = 3; private static final int INVALIDATE = 1; private static final int INITIAL_TIME_DELAY = 100; private static final int TIME_DELAY = 10; // private static final int TIME_STEPS = 30; private boolean mPageTurning; private boolean mStepping; public long mNextTime; private int mTimeStep; private int mDrawnTimeStep; private int mCorner; private Drawable mBackPage; private Drawable mPageBackground; private Path mForegroundPath; private Path mBackPagePath; private Path mBackgroundPath; private float mRotation; private Rect mChildRect = new Rect(); private int mOuterOffsetX; private int mOuterOffsetY; public float mPivotX; private int mPageId; private PageViewP1 mPage; private PointF mPageTurnCorner = new PointF(); private PointF mOppositeCorner = new PointF(); private PointF mPageDim = new PointF(); private int mStepLen = 1; public static final int KEEP = 0; public static final int NEXT = 1; public static final int LAST = 2; public int mWhere = KEEP; public boolean isBgInit = true; public boolean isBackInit = true; private float ax,ay,bx,by,cx,cy,dx,dy,ex,ey,c0x,c0y; private int mMaxStep=30; public final Handler mHandler = new Handler() { public void handleMessage(Message msg) { if (msg.what != 1) { return; } // PageTurnerViewP1.this.invalidate(); refreshUI(); // PageTurnerViewP1.this.invalidate((int)bx, (int)ay, (int)dx, (int)dy); if (PageTurnerViewP1.this.mStepping) { return; } msg = obtainMessage(1); long current = SystemClock.uptimeMillis(); if (PageTurnerViewP1.this.mNextTime < current) { //PageTurnerViewP1.access$102(PageTurnerViewP1.this, current + 10L); PageTurnerViewP1.this.mNextTime= current + 5L; } sendMessageAtTime(msg, PageTurnerViewP1.this.mNextTime); //PageTurnerViewP1.access$114(PageTurnerViewP1.this, 10L); PageTurnerViewP1.this.mNextTime+= 5L; } }; public PageTurnerViewP1(Context context) { super(context); Log.i("==================== PageTurnerViewP1(Context context) =================", "" + this); } public PageTurnerViewP1(Context context, AttributeSet attrs) { super(context, attrs); this.mPageId = -1; this.mCorner = -1; } protected void onFinishInflate() { super.onFinishInflate(); if (this.mPageId != -1) { this.mPage = ((PageViewP1)findViewById(this.mPageId)); if (this.mPage != null) this.mPage.setPageTurner(this); } } public void setPageId(int pageId) { this.mPageId = pageId; this.mPage = ((PageViewP1)findViewById(this.mPageId)); if (this.mPage != null) this.mPage.setPageTurner(this); } public int getPageId() { return this.mPageId; } public void setPage(PageViewP1 pageViewP1) { this.mPage = pageViewP1; } public PageViewP1 getPage() { return this.mPage; } public void setCorner(int corner) { this.mCorner = corner; } public int getCorner() { return this.mCorner; } protected void dispatchDraw(Canvas canvas) { Log.v("log dispatchDraw:", "drawing back page"+mPageTurning); // if ((this.mPageTurning) && (this.mPage != null) && (computePageTurn())) { if ((computePageTurn())&& (this.mPageTurning) && (this.mPage != null) ) { this.mPage.setClipPath(this.mForegroundPath); } super.dispatchDraw(canvas); if (this.mPageTurning) { drawBackground(canvas); drawBackPage(canvas); if (!updateTimeStep()) { this.mHandler.removeMessages(1); if (this.mPage != null) { this.mPage.onPageTurnFinished(canvas); } this.mPageTurning = false; this.mStepping = false; invalidate(); } } } public void startPageTurn(int mTimeStep) { if ((this.mPage == null) && (this.mPageId != -1)) { this.mPage = ((PageViewP1)findViewById(this.mPageId)); } if (this.mPage == null) { return; } this.mPage.setPageTurner(this); Drawable d = this.mPage.getPageBackground(); if (d != null) { this.mPageBackground = d; } d = this.mPage.getBackPage(); if (d != null) { this.mBackPage = d; } int corner = this.mPage.getCorner(); if (corner != -1) { this.mCorner = corner; } // this.mStepping=false; this.mPageTurning = true; this.mTimeStep = mTimeStep; this.mDrawnTimeStep = -1; Message msg = this.mHandler.obtainMessage(1); this.mNextTime = (SystemClock.uptimeMillis() + 5L); this.mHandler.sendMessageAtTime(msg, this.mNextTime); } public void stepPageTurn() { if (!this.mStepping) { this.mStepping = true; startPageTurn(this.mTimeStep); } else { // refreshUI(); } } public void refreshUI(){ computePageTurn(); // this.invalidate(); } private void sendUIhandler(){ Message msg = this.mHandler.obtainMessage(1); this.mNextTime = (SystemClock.uptimeMillis() + 30L); this.mHandler.sendMessageAtTime(msg, this.mNextTime); } private boolean updateTimeStep() { if (this.mTimeStep >mMaxStep || this.mTimeStepthis.mMaxStep)) { if ( (this.mTimeStep this.mMaxStep)) { return false; } if (this.mPage == null) { return false; } this.mDrawnTimeStep = this.mTimeStep; View child = this.mPage.getChildAt(0); child.getDrawingRect(this.mChildRect); // this.mOuterOffsetX = (child.getLeft() - getLeft()); // this.mOuterOffsetY = (child.getTop() - getTop()); this.mOuterOffsetX = 0; this.mOuterOffsetY = 0; float width = this.mChildRect.right; float height = this.mChildRect.bottom; if(!mStepping){ this.mPivotX = (this.mTimeStep / 30.0f * width); } this.mForegroundPath = new Path(); this.mBackPagePath = new Path(); this.mBackgroundPath = new Path(); float slope = width / (this.mPivotX - width); float y = this.mPivotX * slope; this.mPageTurnCorner.x = 0.0F; this.mPageTurnCorner.y = height; this.mOppositeCorner.x = width; this.mOppositeCorner.y = 0.0F; float x0 = this.mPivotX; float cornerIntersect = height * width / (height + width); if ((this.mCorner & 0x1) != 0) { this.mPageTurnCorner.x = width; this.mOppositeCorner.x = 0.0F; x0 = width - x0; } if ((this.mCorner & 0x2) != 0) { this.mPageTurnCorner.y = 0.0F; this.mOppositeCorner.y = height; } this.mPageDim.x = width; this.mPageDim.y = height; float page_slope; if (this.mPivotX 2){ LinearGradient grad = new LinearGradient( ex,ey,ex+(dx-ex)/4,ey+(dy-ey)/4,R.color.gray3,Color.TRANSPARENT,Shader.TileMode.CLAMP); Paint p=new Paint(); p.setShader(grad); // p.setAlpha(120); canvas.drawPath(this.mBackgroundPath,p); //end test } canvas.restore(); } private void drawBackPage(Canvas canvas) { float width = this.mChildRect.right; float height = this.mChildRect.bottom; canvas.save(); // canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG)); canvas.clipPath(this.mBackPagePath, Region.Op.INTERSECT); float xShift = 2.0F * this.mPivotX - width; float xRotate = width - this.mPivotX; float yRotate = height; if ((this.mCorner & 0x1) != 0) { xShift = width - 2.0F * this.mPivotX; xRotate = this.mPivotX; } if ((this.mCorner & 0x2) != 0) { yRotate = 0.0F; } canvas.translate(this.mOuterOffsetX + xShift, this.mOuterOffsetY); canvas.rotate(this.mRotation, xRotate, yRotate); //画原本的背面 if (this.mBackPage != null) { this.mBackPage.setBounds(0, 0, this.mChildRect.right, this.mChildRect.bottom); this.mBackPage.draw(canvas); } //画回调函数中的画背面 if (this.mPage != null) { Log.v("log2 drawBackPage2:", "drawing back page"); this.mPage.drawBackPage(canvas); } canvas.restore(); canvas.save(); // LinearGradient grad = new LinearGradient( ex,ey,ex-(ex-bx)/4,ey-(ey-by)/4,R.color.gray3,0xC9C9C9,Shader.TileMode.CLAMP); Paint p=new Paint(); p.setShader(grad); // p.setAlpha(120); canvas.drawPath(this.mBackPagePath,p); canvas.restore(); //中间阴影问题,起点蓝色-》 白色-》 蓝色终点,这样起点前和终点后的区域也为蓝色了。 canvas.save(); // canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG)); LinearGradient grad1 = new LinearGradient( ex-(ex-bx)/4,ey-(ey-by)/4,bx,by,0xC9C9C9,R.color.gray3,Shader.TileMode.CLAMP); Paint p1=new Paint(); p1.setShader(grad1); // p1.setAlpha(120); canvas.drawPath(this.mBackPagePath,p1); canvas.restore(); // } public int getmTimeStep(){ return mTimeStep; } public void setmTimeStep(int mTimeStep ){ this.mTimeStep= mTimeStep; } public boolean getmStepping(){ return mStepping; } public void setmStepping(boolean mStepping ){ this.mStepping= mStepping; } public void setmStepLen(int mStepLen){ this.mStepLen=mStepLen; } public int getmStepLen(){ return this.mStepLen; } public void setmMaxStep(int mMaxStep){ this.mMaxStep=mMaxStep; } public int getmMaxStep(){ return this.mMaxStep; } public void setPreStart(int where) { switch (where) { case NEXT: case LAST: mWhere = where; break; default: mWhere = KEEP; break; } isBgInit = true; isBackInit = true; } }
第二个view类pageView.java:
public class PageViewP1 extends RelativeLayout { public static final int CORNER_BOTTOM_LEFT = 0; public static final int CORNER_BOTTOM_RIGHT = 1; public static final int CORNER_TOP_LEFT = 2; public static final int CORNER_TOP_RIGHT = 3; private Path mClipPath; private PageTurnerViewP1 mPageTurner; private Callback mCallback; private int mCorner; private Drawable mBackPage; private Drawable mPageBackground; public PageViewP1(Context context) { super(context); } public PageViewP1(Context context, AttributeSet attrs ) { super(context, attrs ); this.mBackPage =this.getBackground(); this.mCorner = -1; } void setPageTurner(PageTurnerViewP1 pageTurnerViewP1) { this.mPageTurner = pageTurnerViewP1; } void setClipPath(Path clipPath) { this.mClipPath = clipPath; } public void setCallback(Callback callback) { this.mCallback = callback; } void drawBackPage(Canvas canvas) { if (this.mCallback != null) this.mCallback.onDrawBackPage(canvas); } void drawBackground(Canvas canvas) { if (this.mCallback != null) this.mCallback.onDrawBackground(canvas); } public void startPageTurn() { if (this.mPageTurner != null) this.mPageTurner.startPageTurn(0); } void onPageTurnFinished(Canvas canvas) { this.mCallback.onPageTurnFinished(canvas); this.mClipPath = null; } protected void dispatchDraw(Canvas canvas) { if (this.mClipPath != null) { canvas.save(); canvas.clipPath(this.mClipPath, Region.Op.INTERSECT); } super.dispatchDraw(canvas); if (this.mClipPath != null) canvas.restore(); } public void setCorner(int corner) { this.mCorner = corner; } public int getCorner() { return this.mCorner; } public void setBackPage(Drawable backPage) { this.mBackPage = backPage; } public Drawable getBackPage() { return this.mBackPage; } public void setPageBackground(Drawable background) { this.mPageBackground = background; } public Drawable getPageBackground() { return this.mPageBackground; } public static abstract class Callback { public void onDrawBackPage(Canvas canvas) { Log.v("Callback", "drawing back page"); } public void onDrawBackground(Canvas canvas) { Log.v("Callback2", "drawing back page"); } public void onPageTurnFinished(Canvas canvas) { Log.v("Callback3", "drawing back page"); } } }
import java.lang.reflect.Field; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; import android.os.Bundle; import android.util.Log; import android.view.Display; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.view.View.OnTouchListener; import android.widget.TextView; public class PageTurnerDemo extends Activity { private PageTurnerViewP1 pageturner; private PageViewP1 page1; private static final String TAG = "RUKIDEMO"; TextView text1; TextView text2; int screenWidth; int screenHeight; String content1; String content2; String currcontent; private boolean marktest = true; private int startX; private int mMaxStep=30; private int mAnimaStepLen=mMaxStep/8; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); getWH(); setContentView(R.layout.main); initPage(); pageturner = (PageTurnerViewP1) findViewById(R.id.pages); pageturner.setPage(page1); pageturner.setLongClickable(true); pageturner.setOnTouchListener(pageontounchlistener); // 自动运行surfaceCreated以及surfaceChanged } private void getWH() { WindowManager windowManager = getWindowManager(); Display display = windowManager.getDefaultDisplay(); screenWidth = display.getWidth(); screenHeight = display.getHeight(); } private void initPage() { content1 =getResources().getString(R.string.content1); content2 = getResources().getString(R.string.content2); // 初始化两页内容 text1 = (TextView) findViewById(R.id.page1text); // text1.setBackgroundResource(R.drawable.solopage); text1.setText(content1); text2 = (TextView) findViewById(R.id.page2text); // text2.setBackgroundResource(R.drawable.solopage); text2.setText(content2); page1 = (PageViewP1) findViewById(R.id.page1); // page1.setBackPage(this.getResources().getDrawable(R.drawable.solopage)); // page1.setPageBackground(this.getResources() // .getDrawable(R.drawable.page)); page1.setCallback(pageturncallback); page1.setCorner(PageViewP1.CORNER_TOP_LEFT); // /++++++++++++++++++++++++++++++++++++++++++++/// } PageViewP1.Callback pageturncallback = new PageViewP1.Callback() { public void onDrawBackPage(Canvas canvas1) { // apparently this gets called after every animation // so if we wanted to we can draw the back page here Log.v(TAG, "drawing back page"); // Paint mpaint = new Paint(); // mpaint.setColor(Color.BLUE); // canvas1.drawText( // "testtesttesttesttesttesttesttesttesttesttesttesttest", // 150, 150, mpaint); // canvas1.drawCircle(0,0,200,p); //参数3为画圆的半径,类型为float型。 // Shader setShader(Shader shader); // text1.draw(canvas1); } public void onDrawBackground(Canvas canvas1) { // apparently this gets called after every animation // so if we wanted to we can draw the background here Log.v(TAG, "drawing background"); // Paint mpaint = new Paint(); // Path path = new Path(); // mpaint.setColor(Color.BLACK); // TextUtil mTextUtil; // String string = // "12345678910111213 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n" // +"12345678910111213 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n" // +"12345678910111213 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n" // +"12345678910111213 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n" // +"12345678910111213 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29\n"; // mTextUtil = new TextUtil(string, 15, 15, 350, 80, 0x000000, // 0xff00ff, // 235, 16); // mTextUtil.InitText(); // mTextUtil.DrawText(canvas1); // mpaint.setTextSize(textSize) // canvas1.drawt // page2.draw(canvas1); canvas1.save(); // Paint p=new Paint(); // LinearGradient lg=new LinearGradient(0,0,100,100,Color.BLACK,Color.WHITE,Shader.TileMode.MIRROR); // //参数一为渐变起初点坐标x位置,参数二为y轴位置,参数三和四分辨对应渐变终点,最后参数为平铺方式,这里设置为镜像 // p.setShader(lg); //// canvas1.translate(200, 200); // canvas1.skew(5, 5); text2.draw(canvas1); // myonDraw(canvas1); canvas1.restore(); } public void onPageTurnFinished(Canvas canvas1) { // this gets called after the animation is done // perphaps this is where we should change view to the new // 在这里在换当前页内容到下一页的内容,这里假设text1是当前页内容 if (marktest) { text1.setText(content2); text2.setText(content1); marktest = !marktest; } else { text2.setText(content2); text1.setText(content1); marktest = !marktest; } } }; OnTouchListener pageontounchlistener = new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub final int action = event.getAction(); final int x = (int) event.getX(); final int y = (int) event.getY(); final float px = event.getX(); int itemLen = screenWidth / mMaxStep; switch (action) { case MotionEvent.ACTION_DOWN: startX=x; // LEFT if (x < screenWidth / 2) { // LT if (y < screenHeight / 2) { page1.setCorner(PageTurnerViewP1.CORNER_TOP_RIGHT); // LB } else { page1.setCorner(PageTurnerViewP1.CORNER_BOTTOM_RIGHT); } // RIGHT } else { // RT if (y < screenHeight / 2) { page1.setCorner(PageTurnerViewP1.CORNER_TOP_RIGHT); // RB } else { page1.setCorner(PageTurnerViewP1.CORNER_BOTTOM_RIGHT); } } break; case MotionEvent.ACTION_MOVE: // pageturner.setPivotX(screenWidth-px); // pageturner.invalidate(); //左侧起 if (startX pageturner.setmStepLen(-2); pageturner.setmTimeStep((screenWidth-x) / itemLen); pageturner.mPivotX=(screenWidth-x); pageturner.stepPageTurn(); //右侧起 } else { pageturner.setmStepLen(2); pageturner.setmTimeStep((screenWidth-x) / itemLen); pageturner.mPivotX=(screenWidth-x); pageturner.stepPageTurn(); } break; case MotionEvent.ACTION_UP: pageturner.setmStepLen(2); pageturner.setmStepping(false); //左侧起 if (startX screenWidth / 3){ pageturner.setmStepLen(-2); }//左松 pageturner.startPageTurn((screenWidth-x) / itemLen); //右侧起 } else { pageturner.setmStepLen(2); //左松 if(x < (screenWidth *2)/ 3){ pageturner.setmStepLen(2); }else {//右松 pageturner.setmStepLen(-2); } pageturner.startPageTurn((screenWidth-x) / itemLen); } break; case MotionEvent.ACTION_CANCEL: } return true; } }; }