实例介绍
【实例截图】
【核心代码】
package com.xiexj.ebook; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Path; import android.graphics.Shader; import android.graphics.Paint.Style; import android.graphics.Shader.TileMode; import android.os.Handler; import android.view.MotionEvent; import android.view.View; public class SinglePage extends View { private boolean isLeftPage = false; private int pageNo; private int x; private int y; private int width; private int height; private Paint paint; private int dis = 10; //当前选中的角 private int chooseCorner = -1; public static final int LEFT_UP_CORNER = 0; public static final int LEFT_BOTTOM_CORNER = 1; public static final int RIGHT_UP_CORNER = 2; public static final int RIGHT_BOTTOM_CORNER = 3; //A点的坐标是手指目前的坐标 private float aX = -1; private float aY = -1; //B点的坐标是A点翻起后跟下一页的交叉点 private float bX = -1; private float bY = -1; //C点的坐标是D点翻起后跟下一页的交叉点,可能与D点重复 private float cX = -1; private float cY = -1; //D点的坐标是本页的另一个角的点的坐标 private float dX = -1; private float dY = -1; //旋转的角度 private float angle = 0; //翻动页背面的阴影角度 private float shadowAngle = 0; //阴影长度 private float shadowLength = 0; //阴影的坐标x private float sdx = 0; //阴影的坐标y private float sdy = 0; //是否隐藏层 private boolean isMask; //是否是当前显示页 private boolean isLookPage; //隐藏层的显示区域 private Path maskPath; //翻动页的fx坐标 private float fx; //翻动页的fy坐标 private float fy; //背景色 private int bc; //显示的角 private int cr; private BookLayout blo; private PageContent content; public SinglePage(Context context,boolean isLeftPage,int pageNo,BookLayout blo,PageContent content) { super(context); this.isLeftPage = isLeftPage; this.pageNo = pageNo; this.blo = blo; this.content = content; paint = new Paint(); bc = Color.WHITE; paint.setARGB(255, Color.red(bc), Color.green(bc), Color.blue(bc)); paint.setAntiAlias(true); isMask = true; isLookPage = false; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //抗锯齿 canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG)); if(isMask){//如果是遮罩层 // if(blo.getCurrentFlipPage()!=null&&this==blo.getMaskPage(blo.getCurrentFlipPage())){ // Log.e("SinglePage", "------->pageNo:" pageNo); // } if(maskPath==null) return; float tx = 0; float ty = 0; switch(cr){ case LEFT_UP_CORNER:{ tx = fx; ty = fy; break; } case RIGHT_UP_CORNER:{ tx = fx-width; ty = fy; break; } case LEFT_BOTTOM_CORNER:{ tx = fx; ty = fy-height; break; } case RIGHT_BOTTOM_CORNER:{ tx = fx-width; ty = fy-height; break; } } canvas.clipPath(maskPath); canvas.save(); canvas.rotate(angle, fx, fy); paint.setStyle(Style.FILL); paint.setARGB(255, Color.red(bc), Color.green(bc), Color.blue(bc)); canvas.drawRect(tx,ty,tx width,ty height, paint); content.setPosition(tx, ty); content.draw(canvas); paint.setStyle(Style.STROKE); paint.setARGB(255,0,0,0); canvas.drawRect(tx,ty,tx width,ty height, paint); if(content!=null&&content.isContentPage()){ if(isLeftPage){ canvas.drawText("第" (pageNo-1) "页", tx 5, ty height-5, paint); }else{ canvas.drawText("第" (pageNo-1) "页", tx width-5-paint.measureText("第" (pageNo-1) "页"), ty height-5, paint); } } canvas.restore(); canvas.save(); canvas.rotate(shadowAngle, sdx, sdy); paint.setStyle(Style.FILL); shadowLength = (float)Math.sqrt(width*width height*height); if(isLeftPage){ Shader linearShader = new LinearGradient(sdx-50,sdy,sdx,sdy,Color.argb(0, 0, 0, 0),Color.argb(33, 0, 0, 0),TileMode.CLAMP); paint.setShader(linearShader); canvas.drawRect(sdx-50, sdy, sdx, sdy shadowLength, paint); }else{ Shader linearShader = new LinearGradient(sdx,sdy,sdx 50,sdy,Color.argb(33, 0, 0, 0),Color.argb(0, 0, 0, 0),TileMode.CLAMP); paint.setShader(linearShader); canvas.drawRect(sdx, sdy, sdx 50, sdy shadowLength, paint); } paint.setShader(null); canvas.restore(); }else{//显示层 Path backPath = null; if(aX!=-1&&aY!=-1){ backPath = new Path(); backPath.moveTo(aX, aY); backPath.lineTo(bX, bY); backPath.lineTo(cX, cY); if(cX!=dX||cY!=dY){ backPath.lineTo(dX, dY); } backPath.lineTo(aX, aY); float px = -1; float py = -1; float ox = -1; float oy = -1; float ex = -1; float ey = -1; switch(chooseCorner){ case RIGHT_UP_CORNER:{ px = x; py = y; ox = x; oy = y height; ex = x width; ey = y height; break; } case RIGHT_BOTTOM_CORNER:{ px = x; py = y height; ox = x; oy = y; ex = x width; ey = y; break; } case LEFT_UP_CORNER:{ px = width x; py = y; ox = width x; oy = height y; ex = x; ey = y height; break; } case LEFT_BOTTOM_CORNER:{ px = width x; py = height y; ox = width x; oy = y; ex = x; ey = y; break; } } maskPath = new Path(); maskPath.moveTo(bX, bY); maskPath.lineTo(px, py); maskPath.lineTo(ox, oy); if(cX==dX&&cY==dY){ maskPath.lineTo(ex, ey); } maskPath.lineTo(cX, cY); maskPath.lineTo(bX, bY); } if(maskPath!=null){ canvas.clipPath(maskPath); canvas.save(); } paint.setStyle(Style.FILL); paint.setARGB(255, Color.red(bc), Color.green(bc), Color.blue(bc)); canvas.drawRect(x,y,x width,y height, paint); paint.setARGB(255,0,0,0); paint.setStyle(Style.STROKE); content.setPosition(x, y); canvas.drawRect(x,y,x width,y height, paint); content.draw(canvas); if(content!=null&&content.isContentPage()){ if(isLeftPage){ canvas.drawText("第" (pageNo-1) "页", x 5, y height-5, paint); }else{ canvas.drawText("第" (pageNo-1) "页", x width-5-paint.measureText("第" (pageNo-1) "页"), y height-5, paint); } } //显示书轴阴影 paint.setStyle(Style.FILL); if(!blo.isFirstOrLastPage(this)){ if(isLeftPage){ Shader linearShader = new LinearGradient(x width-50,y,x width,y,Color.argb(0, 0, 0, 0),Color.argb(33, 0, 0, 0),TileMode.CLAMP); paint.setShader(linearShader); canvas.drawRect(x width-50, y, x width, y height, paint); }else{ Shader linearShader = new LinearGradient(x,y,x 50,y,Color.argb(33, 0, 0, 0),Color.argb(0, 0, 0, 0),TileMode.CLAMP); paint.setShader(linearShader); canvas.drawRect(x, y, x 50, y height, paint); } paint.setShader(null); } if(maskPath!=null){ canvas.restore(); } if(backPath!=null){ float sdx = -1; float sdy = -1; if(bY>cY){ sdy = cY; sdx = cX; }else{ sdy = bY; sdx = bX; } blo.flipPage(this, backPath,angle,shadowAngle,aX,aY,sdx,sdy,chooseCorner); } } } /** * 显示反页遮罩层的区域 * @param path * @param angle * @param x * @param y */ public void onMaskPathDraw(Path path,float angle,float shadowAngle,float x,float y,float sdx,float sdy,int chooseCorner){ this.maskPath = path; this.angle = angle; this.shadowAngle = shadowAngle; this.fx = x; this.fy = y; this.sdx = sdx; this.sdy = sdy; switch(chooseCorner){ case LEFT_UP_CORNER:{ cr = RIGHT_UP_CORNER; break; } case RIGHT_UP_CORNER:{ cr = LEFT_UP_CORNER; break; } case LEFT_BOTTOM_CORNER:{ cr = RIGHT_BOTTOM_CORNER; break; } case RIGHT_BOTTOM_CORNER:{ cr = LEFT_BOTTOM_CORNER; break; } } postInvalidate(); } public void setSize(int w,int h){ width = w; height = h; content.setSize(width, height); } public void setPosition(int x,int y){ this.x = x; this.y = y; fx = x; fy = y; } public void setBackgroundColor(int color){ this.bc = color; } @Override public boolean onTouchEvent(MotionEvent event) { if(!isLookPage||blo.isAutoFlip()){ return false; } int act = event.getAction(); float hx = event.getX(); float hy = event.getY(); switch(act){ case MotionEvent.ACTION_DOWN:{ if(isNearCorner(hx, hy)&&isEnableChoose(hx, hy)){ //初始化翻页的角度 aX = hx; aY = hy; calculate(); postInvalidate(); blo.setCurrentFlipPage(this); } break; } case MotionEvent.ACTION_MOVE:{ if(chooseCorner!=-1&&isEnableChoose(hx, hy)){ aX = hx; aY = hy; calculate(); postInvalidate(); } break; } case MotionEvent.ACTION_UP:{ if(chooseCorner!=-1){ successOrResetPage(); } break; } } if(chooseCorner==-1){ return false; } return true; } //判断是否在页角附近 private boolean isNearCorner(float hx,float hy){ //左上角 int lux = x; int luy = y; //左下角 int lbx = x; int lby = y height; //右上角 int rux = x width; int ruy = y; //右下角 int rbx = x width; int rby = y height; int[][] pd = { {lux,luy}, {lbx,lby}, {rux,ruy}, {rbx,rby} }; for(int i=0;i<pd.length;i ){ //计算是否在页角附近,并返回是哪个页角 if(isLeftPage){ if(i>=2) continue; }else{ if(i<2) continue; } if((pd[i][0]-hx)*(pd[i][0]-hx) (pd[i][1]-hy)*(pd[i][1]-hy)<=2*dis*dis){ chooseCorner = i; return true; } } return false; } //判断是否是有效的翻页坐标 private boolean isEnableChoose(float hx,float hy){ boolean flag = false; switch(chooseCorner){ case LEFT_UP_CORNER:{ if((hx-(width x))*(hx-(width x)) (hy-y)*(hy-y)<=width*width && (hx-(width x))*(hx-(width x)) (hy-(height y))*(hy-(height y))<=width*width height*height){ flag = true; } break; } case LEFT_BOTTOM_CORNER:{ if((hx-(width x))*(hx-(width x)) (hy-(height y))*(hy-(height y))<=width*width && (hx-(width x))*(hx-(width x)) (hy-y)*(hy-y)<=width*width height*height){ flag = true; } break; } case RIGHT_UP_CORNER:{ if((hx-x)*(hx-x) (hy-y)*(hy-y)<=width*width && (hx-x)*(hx-x) (hy-(height y))*(hy-(height y))<=width*width height*height){ flag = true; } break; } case RIGHT_BOTTOM_CORNER:{ if((hx-x)*(hx-x) (hy-(height y))*(hy-(height y))<=width*width && (hx-x)*(hx-x) (hy-y)*(hy-y)<=width*width height*height){ flag = true; } break; } } // Log.e("Page","isEnableChoose " flag); return flag; } //判断是否成功翻页还是取消翻页 private void successOrResetPage(){ float sx = -1; float sy = -1; float fx = -1; float fy = -1; //手松开的时候,如果另一侧的角翻动幅度更大的话,以那个角作为A点,启动自动翻书动作 if(isLeftPage){ if(dX>aX){ aX = dX; aY = dY; if(chooseCorner==LEFT_UP_CORNER){ chooseCorner = LEFT_BOTTOM_CORNER; }else{ chooseCorner = LEFT_UP_CORNER; } } }else{ if(dX<aX){ aX = dX; aY = dY; if(chooseCorner==RIGHT_UP_CORNER){ chooseCorner = RIGHT_BOTTOM_CORNER; }else{ chooseCorner = RIGHT_UP_CORNER; } } } calculate(); //计算成功或失败翻页后,被触碰的页角需要达到的坐标 switch(chooseCorner){ case LEFT_UP_CORNER:{ sx = x width*2; sy = y; fx = x; fy = y; break; } case LEFT_BOTTOM_CORNER:{ sx = x width*2; sy = y height; fx = x; fy = y height; break; } case RIGHT_UP_CORNER:{ sx = x-width; sy = y; fx = x width; fy = y; break; } case RIGHT_BOTTOM_CORNER:{ sx = x-width; sy = y height; fx = x width; fy = y height; break; } } if(isLeftPage&&(aX>=x width*3/2||dX>=x width*3/2)){ autoFlipPage(sx, sy, true); }else if(!isLeftPage&&(aX<=x-width/2||dX<=x-width/2)){ autoFlipPage(sx, sy, true); }else{ autoFlipPage(fx,fy,false); } } //当在翻页过程中,手松开页角的时候,自动执行翻页动作,让翻页更真实 private void autoFlipPage(final float tx,final float ty,final boolean isSuccessFlip){ blo.setAutoFlip(true); new Thread(){ public void run(){ int count = 1; boolean flag = false; while(true){ try { flag=autoMovePageCorner(tx, ty,count ); Thread.sleep(100); if(flag) break; } catch (InterruptedException e) { e.printStackTrace(); } } //清除翻页参数 chooseCorner = -1; clearPosition(); if(isSuccessFlip){ handler.post(new Runnable(){ public void run(){ blo.setCurrentFlipPage(null); blo.successFlipPage(SinglePage.this, true); } }); }else{ handler.post(new Runnable(){ public void run(){ blo.setCurrentFlipPage(null); blo.successFlipPage(SinglePage.this, false); } }); } blo.setAutoFlip(false); } }.start(); } private Handler handler = new Handler(); //线程内部,自动移动页角 private boolean autoMovePageCorner(float tx,float ty,int num){ boolean isFinish = false; if((aX-tx)*(aX-tx) (aY-ty)*(aY-ty)<=dis*dis){ isFinish = true; }else{ handler.post(new Runnable(){ public void run(){ postInvalidate(); } }); aX = aX-num*(aX-tx)/10; aY = aY-num*(aY-ty)/10; calculate(); } return isFinish; } //根据A点计算其余点 private void calculate(){ float px = -1; float py = -1; float ox = -1; float oy = -1; switch(chooseCorner){ case LEFT_UP_CORNER:{ px = x; py = y; ox = x; oy = y height; bY = y; cX = x; cY = y height; break; } case LEFT_BOTTOM_CORNER:{ px = x; py = y height; ox = x; oy = y; bY = y height; cX = x; cY = y; break; } case RIGHT_UP_CORNER:{ px = width x; py = y; ox = width x; oy = height y; bY = y; cX = x width; cY = y height; break; } case RIGHT_BOTTOM_CORNER:{ px = width x; py = height y; ox = width x; oy = y; bY = y height; cX = x width; cY = y; break; } } if(aY==py){ bY = aY; bX = (aX px)/2; dY = oy; dX = aX; cX = (aX ox)/2; cY = oy; angle = 0; return; } MNLine lineOne = MNLine.initLine(aX, aY, px, py); MNLine lineTwo = lineOne.getPBLine((aX px)/2, (aY py)/2); bX = lineTwo.getXbyY(bY); float tempCY = lineTwo.getYbyX(cX); if(tempCY>=y&&tempCY<=y height){//在左或右边交叉,是三角型, cY = tempCY; dX = cX; dY = cY; }else{ cX = lineTwo.getXbyY(cY); //将直线平移,经过页的另一个角 lineOne.change(ox, oy); float[] xAndY = lineOne.getCross(lineTwo); dX = 2*xAndY[0]-ox; dY = 2*xAndY[1]-oy; } if(((Float)bX).isNaN()||((Float)bY).isNaN()||((Float)cX).isNaN()||((Float)cY).isNaN()||((Float)dX).isNaN()||((Float)dY).isNaN()){ clearPosition(); }else{ MNLine lineThree = MNLine.initLine(aX, aY, dX, dY); angle = (float)(Math.atan(lineThree.getA())*180/Math.PI); if(angle>0){ if(aX<=dX&&aY<=dY&&chooseCorner==RIGHT_BOTTOM_CORNER){ angle = 90 angle; }else if(aX>=dX&&aY>=dY&&chooseCorner==LEFT_UP_CORNER){ angle = 90 angle; }else{ angle = angle-90; } }else{ if(aX<=dX&&aY>=dY&&chooseCorner==RIGHT_UP_CORNER){ angle = -90 angle; }else if(aX>=dX&&aY<=dY&&chooseCorner==LEFT_BOTTOM_CORNER){ angle = -90 angle; }else{ angle = angle 90; } } shadowAngle = (float)(Math.atan(lineTwo.getA())*180/Math.PI); if(shadowAngle>0){ shadowAngle = shadowAngle-90; }else{ shadowAngle = shadowAngle 90; } } } public void setMaskPage(boolean flag){ isMask = flag; } public void setLookPage(boolean flag){ isLookPage = flag; isMask = !flag; } public void reset(){ clearPosition(); angle = 0; shadowAngle = 0; shadowLength = 0; fx = x; fy = y; sdx = -1; sdy = -1; cr = -1; maskPath = null; isMask = true; isLookPage = false; } public PageContent getPageContent(){ return content; } private void clearPosition(){ aX = -1; aY = -1; bX = -1; bY = -1; cX = -1; cY = -1; dX = -1; dY = -1; } }
标签: Android
小贴士
感谢您为本站写下的评论,您的评论对其它用户来说具有重要的参考价值,所以请认真填写。
- 类似“顶”、“沙发”之类没有营养的文字,对勤劳贡献的楼主来说是令人沮丧的反馈信息。
- 相信您也不想看到一排文字/表情墙,所以请不要反馈意义不大的重复字符,也请尽量不要纯表情的回复。
- 提问之前请再仔细看一遍楼主的说明,或许是您遗漏了。
- 请勿到处挖坑绊人、招贴广告。既占空间让人厌烦,又没人会搭理,于人于己都无利。
关于好例子网
本站旨在为广大IT学习爱好者提供一个非营利性互相学习交流分享平台。本站所有资源都可以被免费获取学习研究。本站资源来自网友分享,对搜索内容的合法性不具有预见性、识别性、控制性,仅供学习研究,请务必在下载后24小时内给予删除,不得用于其他任何用途,否则后果自负。基于互联网的特殊性,平台无法对用户传输的作品、信息、内容的权属或合法性、安全性、合规性、真实性、科学性、完整权、有效性等进行实质审查;无论平台是否已进行审查,用户均应自行承担因其传输的作品、信息、内容而可能或已经产生的侵权或权属纠纷等法律责任。本站所有资源不代表本站的观点或立场,基于网友分享,根据中国法律《信息网络传播权保护条例》第二十二与二十三条之规定,若资源存在侵权或相关问题请联系本站客服人员,点此联系我们。关于更多版权及免责申明参见 版权及免责申明
网友评论
我要评论