先看效果图: 我先来解释一下该翻页的实现原理,大家来看下面这张图: 我们可以把翻页时的图案分为三部分,分别是第一页的图案,第一页的背面图案,以及第二页的图案。 我们将图形进一步数学化: 其中,c、d、b是以e为控制点的贝塞尔曲线上的点。同样,j、i、k是以h为控制点的贝塞尔曲线上的点。 a为翻角的顶点,线段eh为线段af的中垂线。 根据上图,我们可以对线段af左侧做出假设(另一边也同理): ce=ef/2 p是线段cb的中点 d是线段pe的中点 b是ae和cj的交点 由于红色部分中的曲线db和曲线ik我们无法得知它的函数式,所以红色部分不能直接绘制,但是我们可以绘制出红色部分加黄色部分的区域,记为PathC;黄色区域我们也可以单独绘制(c和d按直线相连),记为PathB。然后我们用clipPath(PathB,Region.Op.DIFFERENCE)来切割出红色区域,Op.DIFFERENCE指的是取PathB中与PathC不相同的区域。 另外一说,绿色区域我们也可以单独绘制。这样一来,三个区域我们就都能得到了。 所以,我们最重要的工作就是计算每个点的坐标,下面我来陈述一下每个点的计算方法: a点是我们已知的,记为(ax,ay) f点是屏幕右下角的点,也是已知的,记为(fx,fy) g是线段af和eh的交点,记为(gx,gy),有gx=(ax+fx)/2,gy=(ay+fy)/2 直线eh的斜率记为Keh,有Keh=(-1)*(fx-ax)/(fy-gy) 直线eh的函数式为:y=Keh*(x-gx)+gy e点记为(ex,ey),ey=fy,带入直线eh函数式可得ex=gx+(fy-gy)/Keh h点记为(hx,hy),hx=fx,带入直线eh函数式可得hy=gy+Keh(fx-gx) c点记为(cx,cy),因为ce=ef/2,则cx=ex-ce=ex-ef/2=ex-(fx-ex)/2,cy=fy j点记为(jx,jy),和c点同理,jh=hf/2,有jy=hy-jh=hy-fh/2=hy-(fy-hy)/2,jx=fx d点记为(dx,dy),因为p是cb中点,d是pe中点,有dx=(ex+px)/2=(ex+(cx+bx)/2)/2,dy=(ey+(cy+by)/2)/2 i点记为(ix,iy),和d点同理,有ix=(hx+(kx+jx)/2)/2,iy=(hy+(ky+jy)/2)/2 直线cj的函数式为:y=Kcj(x-jx)+jy,斜率为Kcj=(jy-cy)/(jx-cx) 直线ae的函数式为:y=Kae(x-ax)+ay,斜率为Kae=(ay-ey)/(ax-ex) 直线ah的函数式为:y=Kah(x-ax)+ay,斜率为Kah=(ay-hy)/(ax-hx) b点记为(bx,by),因为b点cj和ae的交点,计算可得bx=(ay-Kae*ax+Kcj*jx-jy)/(Kcj-Kae),by=Kcj(bx-jx)+jy k点记为(kx,ky),因为k点cj和ah的交点,计算可得kx=(ay-Kah*ax+Kcj*jx-jy)/(Kcj-Kah),ky=Kcj(kx-jx)+jy 以上就是每个点的计算方式,下面我们创建一个PaperPoint类,来将计算过程写成代码: 我们计算好每个点后,就要开始绘制任务了,首先我们来绘制绿色区域的内容: 绘制黄色区域加上红色区域所在的总区域: 用clipPath切分出红色的翻角区域: 到这里,翻页效果已经实现,最后我们来监听点击事件,以此来设置a点坐标: 下面是自定义view源码: MyPoint类: xml布局文件:
public class PaperPoint { //拉拽点 private MyPoint a; //右下角的点 private MyPoint f; //贝塞尔点(e为控制点) private MyPoint c,d,b,e; //贝塞尔点(h为控制点) private MyPoint i,j,k,h; //eh实际为af中垂线,g为ah和af的交点 private MyPoint g; public PaperPoint(){ a=new MyPoint();f=new MyPoint(); g=new MyPoint();e=new MyPoint(); h=new MyPoint();c=new MyPoint(); j=new MyPoint();d=new MyPoint(); i=new MyPoint();b=new MyPoint(); k=new MyPoint(); } //每个点的计算公式 private void calculate(){ g.setX((a.getX()+f.getX())/2); g.setY((a.getY()+f.getY())/2); float slopeKeh=-(f.getX()-a.getX())/(f.getY()-a.getY()); e.setX(g.getX()+(f.getY()-g.getY())/slopeKeh); e.setY(f.getY()); h.setX(f.getX()); h.setY(g.getY()+slopeKeh*(f.getX()-g.getX())); c.setX(e.getX()-(f.getX()-e.getX())/2); c.setY(f.getY()); j.setX(f.getX()); j.setY(h.getY()-(f.getY()-h.getY())/2); float slopeKcj=(j.getY()-c.getY())/(j.getX()-c.getX()); float slopeKae=(a.getY()-e.getY())/(a.getX()-e.getX()); float slopeKah=(a.getY()-h.getY())/(a.getX()-h.getX()); b.setX((a.getY()-slopeKae*a.getX()+slopeKcj*j.getX()-j.getY())/(slopeKcj-slopeKae)); b.setY(slopeKcj*(b.getX()-j.getX())+j.getY()); k.setX((a.getY()-slopeKah*a.getX()+slopeKcj*j.getX()-j.getY())/(slopeKcj-slopeKah)); k.setY(slopeKcj*(k.getX()-j.getX())+j.getY()); d.setX(b.getX()/4+c.getX()/4+e.getX()/2); d.setY(b.getY()/4+c.getY()/4+e.getY()/2); i.setX(j.getX()/4+k.getX()/4+h.getX()/2); i.setY(j.getY()/4+k.getY()/4+h.getY()/2); } public void set(MyPoint a,MyPoint f) { this.a = a; this.f = f; calculate(); } public MyPoint getA() { return a; } public MyPoint getF() { return f; } public MyPoint getC() { return c; } public MyPoint getD() { return d; } public MyPoint getB() { return b; } public MyPoint getE() { return e; } public MyPoint getI() { return i; } public MyPoint getJ() { return j; } public MyPoint getK() { return k; } public MyPoint getH() { return h; } public MyPoint getG() { return g; } }
private PaperPoint pp; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //...... //绘制第一页 canvas.save(); canvas.clipPath(getPathA()); canvas.drawBitmap(bitmapBg,matrix,null); //绘制文章内容 drawArticle(canvas,0); canvas.restore(); //...... } //得到第一页图形 private Path getPathA(){ path.reset(); path.lineTo(0,height); path.lineTo(pp.getC().getX(),height); path.quadTo(pp.getE().getX(),pp.getE().getY(),pp.getB().getX(),pp.getB().getY()); path.lineTo(pp.getA().getX(),pp.getA().getY()); path.lineTo(pp.getK().getX(),pp.getK().getY()); path.quadTo(pp.getH().getX(),pp.getH().getY(),pp.getJ().getX(),pp.getJ().getY()); path.lineTo(width,0); path.close(); return path; }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //...... //绘制第二页和翻转背面的内容 canvas.save(); canvas.clipPath(getPathC()); canvas.drawBitmap(bitmapBg,matrix,null); drawArticle(canvas,1); canvas.restore(); //...... } //得到第二页和翻转背面的图形 private Path getPathC(){ pathC.reset(); pathC.moveTo(pp.getJ().getX(),pp.getJ().getY()); pathC.quadTo(pp.getH().getX(),pp.getH().getY(), pp.getK().getX(), pp.getK().getY()); pathC.lineTo(pp.getA().getX(),pp.getA().getY()); pathC.lineTo(pp.getB().getX(),pp.getB().getY()); pathC.quadTo(pp.getE().getX(),pp.getE().getY(),pp.getC().getX(),pp.getC().getY()); pathC.lineTo(pp.getF().getX(),pp.getF().getY()); pathC.close(); return pathC; }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //...... //绘制第一页的背面 canvas.save(); canvas.clipPath(pathC); canvas.clipPath(getPathB(), Region.Op.DIFFERENCE);//difference最取出两段path中不同的地方 canvas.drawBitmap(bitmapBg,matrix,null); canvas.restore(); } //得到第二页的图形 private Path getPathB(){ pathB.reset(); pathB.moveTo(pp.getC().getX(), pp.getC().getY()); pathB.lineTo(pp.getD().getX(),pp.getD().getY()); pathB.lineTo(pp.getI().getX(),pp.getI().getY()); pathB.lineTo(pp.getJ().getX(),pp.getJ().getY()); pathB.lineTo(pp.getF().getX(),pp.getF().getY()); pathB.close(); return pathB; }
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_MOVE: pp.set(new MyPoint(event.getX(),event.getY()),new MyPoint(Constants.SCREEN_WIDTH,Constants.SCREEN_HEIGHT)); invalidate(); break; } return true; }
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Region; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.hualinfo.bean.beizer.MyPoint; import com.hualinfo.utils.PaperPoint; import androidx.annotation.Nullable; public class MyBeizerView extends View { private PaperPoint pp; private Path path,pathB,pathC; //第一页路径,第二页路径,第二页和翻转背面的路径 private Paint txtPaint; private Matrix matrix; private int width= Constants.SCREEN_WIDTH; private int height= Constants.SCREEN_HEIGHT; private String[] strs=new String[2]; //文本 private Bitmap bitmapBg; public MyBeizerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } private void init(){ initBitmap(); path=new Path();pathB=new Path();pathC=new Path(); txtPaint=new Paint(); txtPaint.setColor(Color.WHITE); txtPaint.setTextSize(sp2px(16)); pp=new PaperPoint(); pp.set(new MyPoint(Constants.SCREEN_WIDTH,Constants.SCREEN_HEIGHT),new MyPoint(Constants.SCREEN_WIDTH,Constants.SCREEN_HEIGHT)); strs[0]=getResources().getString(R.string.str2); strs[1]=getResources().getString(R.string.str3); } private void initBitmap(){ matrix=new Matrix(); bitmapBg= BitmapFactory.decodeResource(getResources(),R.mipmap.bg_article); float scaleX=1,scaleY=1; //如果图片与圆的直径不一致,等比例缩放图片 if(bitmapBg.getWidth()!=width||bitmapBg.getHeight()!=height){ scaleX=width/(bitmapBg.getWidth()*1.0f); scaleY=height/(bitmapBg.getHeight()*1.0f); } matrix.setScale(scaleX,scaleY); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(pp.getA().getX()==Constants.SCREEN_WIDTH&&pp.getA().getY()==Constants.SCREEN_HEIGHT){ canvas.drawBitmap(bitmapBg,matrix,null); drawArticle(canvas,0); return; } //绘制第一页 canvas.save(); canvas.clipPath(getPathA()); canvas.drawBitmap(bitmapBg,matrix,null); //绘制文章内容 drawArticle(canvas,0); canvas.restore(); //绘制第二页和翻转背面的内容 canvas.save(); canvas.clipPath(getPathC()); canvas.drawBitmap(bitmapBg,matrix,null); drawArticle(canvas,1); canvas.restore(); //绘制第一页的背面 canvas.save(); canvas.clipPath(pathC); canvas.clipPath(getPathB(), Region.Op.DIFFERENCE);//difference最取出两段path中不同的地方 canvas.drawBitmap(bitmapBg,matrix,null); canvas.restore(); } //绘制文章的文本 private void drawArticle(Canvas canvas,int pos){ int lineNum=(int)(getWidth()/txtPaint.getTextSize()); int size=strs[pos].length()/lineNum; for(int i=0;i<=size;i++){ int endPos=(i+1)*lineNum; if(endPos>=strs[pos].length())endPos=strs[pos].length()-1; canvas.drawText(strs[pos],i*lineNum,endPos,0,sp2px(25*i+20),txtPaint); } } //得到第一页图形 private Path getPathA(){ path.reset(); path.lineTo(0,height); path.lineTo(pp.getC().getX(),height); path.quadTo(pp.getE().getX(),pp.getE().getY(),pp.getB().getX(),pp.getB().getY()); path.lineTo(pp.getA().getX(),pp.getA().getY()); path.lineTo(pp.getK().getX(),pp.getK().getY()); path.quadTo(pp.getH().getX(),pp.getH().getY(),pp.getJ().getX(),pp.getJ().getY()); path.lineTo(width,0); path.close(); return path; } //得到第二页的图形 private Path getPathB(){ pathB.reset(); pathB.moveTo(pp.getC().getX(), pp.getC().getY()); pathB.lineTo(pp.getD().getX(),pp.getD().getY()); pathB.lineTo(pp.getI().getX(),pp.getI().getY()); pathB.lineTo(pp.getJ().getX(),pp.getJ().getY()); pathB.lineTo(pp.getF().getX(),pp.getF().getY()); pathB.close(); return pathB; } //得到第二页和翻转背面的图形 private Path getPathC(){ pathC.reset(); pathC.moveTo(pp.getJ().getX(),pp.getJ().getY()); pathC.quadTo(pp.getH().getX(),pp.getH().getY(), pp.getK().getX(), pp.getK().getY()); pathC.lineTo(pp.getA().getX(),pp.getA().getY()); pathC.lineTo(pp.getB().getX(),pp.getB().getY()); pathC.quadTo(pp.getE().getX(),pp.getE().getY(),pp.getC().getX(),pp.getC().getY()); pathC.lineTo(pp.getF().getX(),pp.getF().getY()); pathC.close(); return pathC; } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: pp.set(new MyPoint(event.getX(),event.getY()),new MyPoint(Constants.SCREEN_WIDTH,Constants.SCREEN_HEIGHT)); invalidate(); break; case MotionEvent.ACTION_UP: break; } return true; } /** * 将sp值转换为px值 */ public int sp2px(float spValue) { float fontScale = getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } }
public class MyPoint { private float x; private float y; public MyPoint() { } public MyPoint(float x, float y) { this.x = x; this.y = y; } public float getX() { return x; } public void setX(float x) { this.x = x; } public float getY() { return y; } public void setY(float y) { this.y = y; } }
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.myviewtext.MyBeizerView android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算