1. 前言

欲整理和实现Android端的翻页效果实现,并想将之整理打包成为一个成熟的第三方插件。不知道会用多少时间来实现这个功能,虽然网上已经有现成的项目,以及对之的解析,但本人从学习的角度来说,不适合直接拷贝集成别人的库来使用,应该抱着学习的心态来学习和整理,并加入自己的想法。我想这应该是接下来我应该做的事情。算是一个比较正式的自学研究历程的笔记。本篇中为翻页的基础篇讲解。

2. 简介


在上图中,我们标明了涉及到翻页所使用的A、B、C三面,以及这三面对应的绘制区域。具体的可参考链接https://juejin.cn/post/6844903529715335182。根据我对它的理解,这里做一个简单的剖析。首先为了简单实现这个静态的效果,所使用到的技术有:
自定义View;

  • 自定义路径;
  • canvas中的路径裁剪;
  • 二阶贝塞尔曲线;

2.1 解析

    在实现前,首先需要明白一个问题就是:翻页这个实现其实看似有这三个页面,其实本质有一个View根布局文件,也就是在一个自定义View中实现。而A、B、C三面是这个页面中的三个部分,也就是我们需要找到各面对应的绘制区域,然后使用canvas.drawBitmap绘图函数将之放置到对应的区域即可。也就是说,其实也就是需要在一个View中找到A、B、C三个区域的Path,然后将每个区域都绘制对应Path的Bitmap图像,并在这个区域中放置对应的文本和背景颜色即可。

根据上图,我们知道这三个区域可以使用图中的这些关键点来进行标识,也就是说只要找到了图中的这些点的坐标,那么我们就可以在一个View中绘制出对应的三个区域A、B、C。那么,我们首先需要解决的问题就是如何来找到图中标识三个区域的关键点。按照大佬的博客Android自定义View——从零开始实现书籍翻页效果(一)以及Android 实现书籍翻页效果----原理篇我们知道可以通过添加一些参考线,利用已知点信息,以及相似性关系可以推测出其余点的信息。那么,这里也一样,学习下这个过程。在下图中,我们添加了一些参考线以及对应的点,接下来就来记录下这个计算过程。

上图中,简单的形式化任务为根据已知点和预设的信息进行目标点的坐标计算。

2.2 计算目标和过程

已知点有:o、a、h。
目标点有:b、c、d、e、f、g、i、j。
预设信息:cg垂直ah,且s为ah的中心。也就是cg是ah的垂直平分线。
记已知点的坐标表示为: a ( x , y ) a(x, y) a(x,y), h ( W , H ) h(W, H) h(W,H)
记任意两点a,b之间的直线长度表示为: l a , b l_{a, b} la,b​
记任意点a的坐标中,横纵坐标表示分别为: ( a x , a y ) (a_x, a_y) (ax​,ay​)
那么,s点的坐标为:
s ( a x + h x 2 , a y + h y 2 ) s(\frac{a_x + h_x}{2}, \frac{a_y + h_y}{2}) s(2ax​+hx​​,2ay​+hy​​)
由于三角形csk相似于三角形shk,那么:
l s , k l c , k = l k , h l s , k \frac{l_{s,k}}{l_{c,k}} = \frac{l_{k,h}}{l_{s,k}} lc,k​ls,k​​=ls,k​lk,h​​
那么, l c , k = l s , k 2 l k , h l_{c,k} = \frac{l_{s,k}^2}{l_{k,h}} lc,k​=lk,h​ls,k2​​ ,且 l s , k = H − s y l_{s,k} = H - s_y ls,k​=H−sy​, l k , h = W − s x l_{k,h} = W - s_x lk,h​=W−sx​,那么 l c , k = ( H − s y ) 2 W − s x l_{c,k} = \frac{(H - s_y)^2}{W - s_x} lc,k​=W−sx​(H−sy​)2​。故而,c点的横坐标长度为 W − l c , k − l k , h = s x − ( H − s y ) 2 W − s x W - l_{c, k} - l_{k, h} = s_x - \frac{(H - s_y)^2}{W - s_x} W−lc,k​−lk,h​=sx​−W−sx​(H−sy​)2​,c点坐标为:
c ( s x − ( H − s y ) 2 W − s x , H ) c(s_x - \frac{(H - s_y)^2}{W - s_x}, H) c(sx​−W−sx​(H−sy​)2​,H)
同理,由于三角形hsm相似于三角形sgm,那么:
l s , m l m , h = l g , m l s , m \frac{l_{s,m}}{l_{m,h}} = \frac{l_{g,m}}{l_{s,m}} lm,h​ls,m​​=ls,m​lg,m​​
那么, l g , m = l s , m 2 l m , h l_{g,m} = \frac{l_{s,m}^2}{l_{m, h}} lg,m​=lm,h​ls,m2​​,且 l s , m = W − s x l_{s,m} = W - s_x ls,m​=W−sx​, l m , h = l s , k = H − s y l_{m, h} = l_{s,k} = H - s_y lm,h​=ls,k​=H−sy​,那么 l g , m = ( W − s x ) 2 H − s y l_{g,m} = \frac{(W - s_x)^2}{H - s_y} lg,m​=H−sy​(W−sx​)2​,故而g点的纵坐标长度为 H − l g , m − l m , h = s y − ( W − s x ) 2 H − s y H - l_{g,m} - l_{m, h} = s_y - \frac{(W - s_x)^2}{H - s_y} H−lg,m​−lm,h​=sy​−H−sy​(W−sx​)2​,g点坐标为:
g ( W , s y − ( W − s x ) 2 H − s y ) g(W,s_y - \frac{(W - s_x)^2}{H - s_y}) g(W,sy​−H−sy​(W−sx​)2​)
设r为线段as的中点,那么三角形brh相似于三角形csh,可以得到相似性关系为:
l b , h l c , h = l r , h l s , h = 3 2 \frac{l_{b, h}}{l_{c, h}} = \frac{l_{r, h}}{l_{s, h}} = \frac{3}{2} lc,h​lb,h​​=ls,h​lr,h​​=23​
那么, l b , h = 3 2 l c , h = 3 2 ( l c , k + l k , h ) = 3 2 ( l c , k + l s , m ) l_{b, h} = \frac{3}{2} l_{c, h} = \frac{3}{2} (l_{c, k} + l_{k, h}) = \frac{3}{2} (l_{c, k} + l_{s, m}) lb,h​=23​lc,h​=23​(lc,k​+lk,h​)=23​(lc,k​+ls,m​),即:
l b , h = 3 2 ( ( H − s y ) 2 W − s x + W − s x ) l_{b, h} = \frac{3}{2} (\frac{(H - s_y)^2}{W - s_x} + W - s_x) lb,h​=23​(W−sx​(H−sy​)2​+W−sx​)
那么,b点坐标为:
b ( W − l b , h , H ) = ( 1 2 ( 3 s x − W − 3 ( H − s y ) 2 W − s x ) , H ) b(W - l_{b, h}, H) = (\frac{1}{2}(3s_x - W - 3\frac{(H - s_y)^2}{W - s_x}), H) b(W−lb,h​,H)=(21​(3sx​−W−3W−sx​(H−sy​)2​),H)
由三角形rfh相似于三角形sgh,那么可以得到相似性关系:
l f , h l g , h = l r , h l s , h = 3 2 \frac{l_{f, h}}{l_{g, h}} = \frac{l_{r, h}}{l_{s, h}} = \frac{3}{2} lg,h​lf,h​​=ls,h​lr,h​​=23​
那么, l f , h = 3 2 l g , h = 3 2 ( l g , m + l m , h ) = 3 2 ( l g , m + l s , k ) l_{f, h} = \frac{3}{2} l_{g, h} = \frac{3}{2} (l_{g, m} + l_{m, h}) = \frac{3}{2} (l_{g, m} + l_{s, k}) lf,h​=23​lg,h​=23​(lg,m​+lm,h​)=23​(lg,m​+ls,k​),即:
l f , h = 3 2 ( ( W − s x ) 2 H − s y + H − s y ) l_{f, h} = \frac{3}{2} (\frac{(W - s_x)^2}{H - s_y} + H - s_y) lf,h​=23​(H−sy​(W−sx​)2​+H−sy​)
那么,f点坐标为:
f ( W , H − l f , h ) = ( W , 1 2 ( 3 s y − H − 3 ( W − s x ) 2 H − s y ) ) f(W, H - l_{f, h}) = (W, \frac{1}{2}(3s_y - H - 3\frac{(W - s_x)^2}{H - s_y})) f(W,H−lf,h​)=(W,21​(3sy​−H−3H−sy​(W−sx​)2​))
直线ac与直线ag分别交直线bf于点d和e,故而可通过直线求交点得到d和e的坐标表示。通过构建辅助线ic和jg,由于垂直关系可以得到i。

直线方程为 y = k x + b y = kx + b y=kx+b,设已知两点 ( x 1 , y 1 ) (x_1, y_1) (x1​,y1​), ( x 2 , y 2 ) (x_2, y_2) (x2​,y2​),那么:
k = y 2 − y 1 x 2 − x 1 k = \frac{y_2 - y_1}{x_2 - x_1} k=x2​−x1​y2​−y1​​
b = y 1 − y 2 − y 1 x 2 − x 1 x 1 b = y_1 - \frac{y_2 - y_1}{x_2 - x_1} x_1 b=y1​−x2​−x1​y2​−y1​​x1​
对于直线ac和ag,a的坐标为 ( x , y ) (x, y) (x,y),c的坐标为 c ( s x − ( H − s y ) 2 W − s x , H ) c(s_x - \frac{(H - s_y)^2}{W - s_x}, H) c(sx​−W−sx​(H−sy​)2​,H),g的坐标为 g ( W , s y − ( W − s x ) 2 H − s y ) g(W,s_y - \frac{(W - s_x)^2}{H - s_y}) g(W,sy​−H−sy​(W−sx​)2​),记未知数为X和Y,那么可以计算到直线ac:
Y 1 = ( H − y s x − ( H − s y ) 2 W − s x − x ) X 1 + ( y − ( ( H − y s x − ( H − s y ) 2 W − s x − x ) ) x ) Y_1 = (\frac{H - y}{s_x - \frac{(H - s_y)^2}{W - s_x} - x})X_1 + (y - ((\frac{H - y}{s_x - \frac{(H - s_y)^2}{W - s_x} - x}))x) Y1​=(sx​−W−sx​(H−sy​)2​−xH−y​)X1​+(y−((sx​−W−sx​(H−sy​)2​−xH−y​))x)
同理,直线ag的表示为:
Y 2 = ( s y − ( W − s x ) 2 H − s y − y W − x ) X 2 + ( y − ( s y − ( W − s x ) 2 H − s y − y W − x ) x ) Y_2 = (\frac{s_y - \frac{(W - s_x)^2}{H - s_y} - y}{W - x})X_2 + (y - (\frac{s_y - \frac{(W - s_x)^2}{H - s_y} - y}{W - x})x) Y2​=(W−xsy​−H−sy​(W−sx​)2​−y​)X2​+(y−(W−xsy​−H−sy​(W−sx​)2​−y​)x)
但是,观察到上面式子比较复杂,那么可以直接简化为:
k 1 = c y − a y c x − a x k_1 = \frac{c_y - a_y}{c_x - a_x} k1​=cx​−ax​cy​−ay​​
b 1 = a y − k 1 a x b_1 = a_y - k_1 a_x b1​=ay​−k1​ax​
Y 1 = k 1 X 1 + b 1 = c y − a y c x − a x X 1 + a y − c y − a y c x − a x a x Y_1 = k_1X_1+b_1 = \frac{c_y - a_y}{c_x - a_x} X_1 + a_y - \frac{c_y - a_y}{c_x - a_x} a_x Y1​=k1​X1​+b1​=cx​−ax​cy​−ay​​X1​+ay​−cx​−ax​cy​−ay​​ax​
类似的,对于 Y 2 Y_2 Y2​有:
Y 2 = g y − a y g x − a x X 2 + a y − g y − a y g x − a x a x Y_2 = \frac{g_y - a_y}{g_x - a_x} X_2 + a_y - \frac{g_y - a_y}{g_x - a_x} a_x Y2​=gx​−ax​gy​−ay​​X2​+ay​−gx​−ax​gy​−ay​​ax​
同理,对于直线bf,可计算到直线bf的表示:
Y 3 = b y − f y b x − f x X 3 + f y − b y − f y b x − f x f x Y_3 = \frac{b_y - f_y}{b_x - f_x} X_3 + f_y - \frac{b_y - f_y}{b_x - f_x} f_x Y3​=bx​−fx​by​−fy​​X3​+fy​−bx​−fx​by​−fy​​fx​
那么,可以计算出 Y 1 Y_1 Y1​与 Y 3 Y_3 Y3​以及 Y 2 Y_2 Y2​与 Y 3 Y_3 Y3​对应的交点d、e的坐标表示。
x = b 1 − b 2 k 2 − k 1 x = \frac{b_1 - b_2}{k_2 - k_1} x=k2​−k1​b1​−b2​​
y = k 1 b 1 − b 2 k 2 − k 1 + b 1 y = k_1 \frac{b_1 - b_2}{k_2 - k_1} + b_1 y=k1​k2​−k1​b1​−b2​​+b1​
即d点坐标:
d ( b 1 − b 3 k 3 − k 1 , k 1 b 1 − b 3 k 3 − k 1 + b 1 ) d(\frac{b_1 - b_3}{k_3 - k_1}, k_1 \frac{b_1 - b_3}{k_3 - k_1} + b_1) d(k3​−k1​b1​−b3​​,k1​k3​−k1​b1​−b3​​+b1​)
e点坐标:
e ( b 2 − b 3 k 3 − k 2 , k 2 b 2 − b 3 k 3 − k 2 + b 2 ) e(\frac{b_2 - b_3}{k_3 - k_2}, k_2 \frac{b_2 - b_3}{k_3 - k_2} + b_2) e(k3​−k2​b2​−b3​​,k2​k3​−k2​b2​−b3​​+b2​)
对于ij两点,由于ic、gj直线和bf垂直,可以求的其斜率 k 4 = − 1 k 3 k_4 = \frac{-1}{k_3} k4​=k3​−1​
不妨假设i是其中点,那么我们需要求到ic与bf的交点,设交点为 p 1 p_1 p1​、 p 2 p_2 p2​:
直线ic的表示为:
Y 4 = − 1 k 3 X 4 + c y − k 4 c x Y_4 = \frac{-1}{k_3} X_4 + c_y - k_4 c_x Y4​=k3​−1​X4​+cy​−k4​cx​
求的 Y 3 Y_3 Y3​和 Y 4 Y_4 Y4​的交点为:
p 1 ( b 4 − b 3 k 3 − k 4 , k 4 b 4 − b 3 k 3 − k 4 + b 4 ) p_1(\frac{b_4 - b_3}{k_3 - k_4}, k_4 \frac{b_4 - b_3}{k_3 - k_4} + b_4) p1​(k3​−k4​b4​−b3​​,k4​k3​−k4​b4​−b3​​+b4​)
那么,可以得到i的坐标表示为:
i ( p 1 x + c x 2 , p 1 y + c y 2 ) i(\frac{p_{1x} + c_x}{2}, \frac{p_{1y} + c_y}{2}) i(2p1x​+cx​​,2p1y​+cy​​)
同理,对于直线gj,斜率 k 5 = k 4 = − 1 k 3 k_5 = k_4 = \frac{-1}{k_3} k5​=k4​=k3​−1​
Y 5 = − 1 k 3 X 5 + g y − k 5 g x Y_5 = \frac{-1}{k_3} X_5 + g_y - k_5 g_x Y5​=k3​−1​X5​+gy​−k5​gx​
求的 Y 3 Y_3 Y3​和 Y 4 Y_4 Y4​的交点为:
p 2 ( b 5 − b 3 k 3 − k 5 , k 5 b 5 − b 3 k 3 − k 5 + b 5 ) p_2(\frac{b_5 - b_3}{k_3 - k_5}, k_5 \frac{b_5 - b_3}{k_3 - k_5} + b_5) p2​(k3​−k5​b5​−b3​​,k5​k3​−k5​b5​−b3​​+b5​)
那么,可以得到j的坐标表示为:
j ( p 2 x + g x 2 , p 2 y + g y 2 ) j(\frac{p_{2x} + g_x}{2}, \frac{p_{2y} + g_y}{2}) j(2p2x​+gx​​,2p2y​+gy​​)
然后,根据这个我们可以创建一个计算的工具类。

3. 代码实现

前面,我们对绘制过程中的点的坐标进行了计算表达的推导,这里就可以根据上面的公式进行点具体值的计算。这里写一个工具类MyPoint,如下:

public class MyPoint {public float x;public float y;public MyPoint(){}public MyPoint(float x, float y){this.x = x;this.y = y;}@Overridepublic String toString() {return "MyPoint{" + "x=" + x + ", y=" + y + '}';}
}

然后,对于怎么计算待求的点的坐标,不妨创建一个工具类来处理,如下:

package com.example.utils;import android.util.Log;
import com.example.pojo.MyPoint;
import java.util.HashMap;
import java.util.Map;public class CalcPoints {// 传入数据private MyPoint a, h;// 目标private MyPoint b, c, d, e, f, g, i, j;private Map<String, MyPoint> points;public Map<String, MyPoint> calcuation(){MyPoint s = new MyPoint((a.x + h.x) / 2, (a.y + h.y) / 2);c.x = s.x - (h.y - s.y) * (h.y - s.y) / (h.x - s.x);c.y = h.y;g.x = h.x;g.y = s.y - (h.x - s.x) * (h.x - s.x) / (h.y - s.y);b.x = 0.5f * (3*s.x - h.x - 3*(h.y - s.y)*(h.y - s.y) / (h.x - s.x));if(b.x <= 0){return null;}b.y = h.y;f.x = h.x;f.y = 0.5f * (3*s.y - h.y - 3 * (h.x - s.x) * (h.x - s.x) / (h.y - s.y));float k_1 = (c.y - a.y) / (c.x - a.x);float b_1 = a.y - k_1 * a.x;float k_2 = (g.y - a.y) / (g.x - a.x);float b_2 = a.y - k_2 * a.x;float k_3 = (b.y - f.y) / (b.x - f.x);float b_3 = f.y - k_3 * f.x;Log.e("k_3", String.valueOf(k_3));Log.e("k_1", String.valueOf(k_1));d.x = (b_1 - b_3) / (k_3 - k_1);d.y = k_1*d.x+b_1;e.x = (b_2 - b_3) / (k_3 - k_2);e.y = k_2 * e.x + b_2;float k_4 = -1.0f / k_3;float b_4 = c.y - k_4 * c.x;MyPoint p_1 = new MyPoint();p_1.x = (b_3 - b_4) / (k_4 - k_3);p_1.y = k_3 * p_1.x + b_3;i.x = (p_1.x + c.x) / 2;i.y = (p_1.y + c.y) / 2;float k_5 = -1.0f / k_3;float b_5 = g.y - k_5 * g.x;MyPoint p_2 = new MyPoint();p_2.x = (b_3 - b_5) / (k_5 - k_3);p_2.y = k_3 * p_2.x + b_3;j.x = (p_2.x + g.x) / 2;j.y = (p_2.y + g.y) / 2;points.put("b", b);points.put("c", c);points.put("d", d);points.put("e", e);points.put("f", f);points.put("g", g);points.put("i", i);points.put("j", j);for (String ss : points.keySet()) {Log.e(ss, points.get(ss).toString());}return points;}public CalcPoints(MyPoint a, MyPoint h){this.h = h;this.a = a;b = new MyPoint();c = new MyPoint();d = new MyPoint();e = new MyPoint();f = new MyPoint();g = new MyPoint();i = new MyPoint();j = new MyPoint();this.points = new HashMap<>();}
}

通过上面的代码可以得到我们绘制A、B、C三面的所有点的坐标,然后就可以开始进入到对应的A、B、C三面的绘制。绘制这种比较复杂的路径图像,需要使用Android的Path类来解决。对于A面,我们定义路径如下:

  • 从左下角出发,沿bidaejf最后回到o;
  • 对于直线,可以简单的使用path.lineTo绘制;
  • 对于曲线,这里使用二阶贝塞尔曲线,控制点分别是c和g;

那么,对应的绘制路径为:

private Path getPathA(){path.reset();path.lineTo(0, viewHeight);path.lineTo(points.get("b").x,points.get("b").y);path.quadTo(points.get("c").x,points.get("c").y, points.get("d").x, points.get("d").y);path.lineTo(a.x, a.y);path.lineTo(points.get("e").x, points.get("e").y);path.quadTo(points.get("g").x,points.get("g").y, points.get("f").x, points.get("f").y);path.lineTo(viewWidth,0);path.close();return path;
}

如下图:

A面绘制,颜色为绿色。BC面为预设的背景色

对于B面,也就在第二个图中的蓝色部分的绘制。但是,这个面比较不好绘制,因为包含两段二阶贝塞尔曲线的一半,一个直线。难点在就在于曲线的绘制。故而考虑将B面暂定为上图的背景色的部分,也就是图第二个图中的B和C两个部分。那么,此时可以用二阶贝塞尔曲线来进行绘制,绘制代码如下:

private Path getPathB(){path.reset();path.lineTo(0, viewHeight);path.lineTo(points.get("b").x,points.get("b").y);path.quadTo(points.get("c").x,points.get("c").y, points.get("d").x, points.get("d").y);path.lineTo(a.x,a.y);path.lineTo(points.get("e").x, points.get("e").y);path.quadTo(points.get("g").x,points.get("g").y, points.get("f").x, points.get("f").y);path.lineTo(viewWidth,viewHeight);path.lineTo(0, viewHeight);path.close();return path;
}

只绘制B面,如下图所示:

对于C面,这部分内容我们在B中绘制过了,也就是在刚刚绘制的B面中包含了目标C面。且那会所说的一半的二阶贝塞尔曲线难以绘制问题依然存在。故而,这里考虑使用Android中的裁剪技术,也就是我们绘制直线构成的区域:idaeji。

private Path getPathC(){path.reset();path.moveTo(points.get("i").x, points.get("i").y);path.lineTo(points.get("d").x, points.get("d").y);path.lineTo(a.x,a.y);path.lineTo(points.get("e").x, points.get("e").y);path.lineTo(points.get("j").x, points.get("j").y);path.close();return path;
}

然后,将这个区域和刚刚的B面进行交集计算,即可得到我们所需要的C面。由于Android的裁剪技术对应canvas.clipPath,也就是作用对象是画布对象。也就是:

@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);Path path = getPathC();bitmap1 = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);Canvas bitmapCanvas1 = new Canvas(bitmap1);bitmapCanvas1.drawPath(getPathA(), mAPaint); // GREEN  Acanvas.drawBitmap(bitmap1,0,0,null);bitmap2 = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);Canvas bitmapCanvas2 = new Canvas(bitmap2);bitmapCanvas2.drawPath(getPathB(), mBPaint); // RED  B// 指定裁剪区域为B+Ccanvas.clipPath(getPathB(), Region.Op.INTERSECT); canvas.drawBitmap(bitmap2,0,0,null);bitmap3 = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);Canvas bitmapCanvas3 = new Canvas(bitmap3);bitmapCanvas3.drawPath(getPathC(), mCPaint); // YELLOW  C// 指定裁剪区域为C,取和上次的裁剪区域的交集canvas.clipPath(getPathC(), Region.Op.INTERSECT); canvas.drawBitmap(bitmap3,0,0,null);
}

效果:

以上View的绘制在一个类中进行,完整代码如下:

package com.example.customview;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.View;import androidx.annotation.Nullable;import com.example.pojo.MyPoint;
import com.example.utils.CalcPoints;import java.util.Map;public class Learn extends View {private Paint mAPaint;private Paint mBPaint;private Paint mCPaint;private MyPoint a, h;private Map<String, MyPoint> points;private Path path;private Bitmap bitmap1;private Bitmap bitmap2;private Bitmap bitmap3;private int viewWidth = 450;private int viewHeight = 600;public Learn(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}private void init(){mAPaint = new Paint();mAPaint.setColor(Color.GREEN);mAPaint.setAntiAlias(true);//设置抗锯齿mBPaint = new Paint();mBPaint.setColor(Color.RED);mBPaint.setAntiAlias(true);//设置抗锯齿mCPaint = new Paint();mCPaint.setColor(Color.YELLOW);mCPaint.setAntiAlias(true);//设置抗锯齿a = new MyPoint(160, 400);h = new MyPoint(viewWidth, viewHeight);points = new CalcPoints(a, h).calcuation();path = new Path();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Path path = getPathC();bitmap1 = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);Canvas bitmapCanvas1 = new Canvas(bitmap1);bitmapCanvas1.drawPath(getPathA(), mAPaint); // GREEN  Acanvas.drawBitmap(bitmap1,0,0,null);bitmap2 = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);Canvas bitmapCanvas2 = new Canvas(bitmap2);bitmapCanvas2.drawPath(getPathB(), mBPaint); // RED  Bcanvas.clipPath(getPathB(), Region.Op.INTERSECT); // 指定裁剪区域为B+Ccanvas.drawBitmap(bitmap2,0,0,null);bitmap3 = Bitmap.createBitmap((int) viewWidth, (int) viewHeight, Bitmap.Config.ARGB_8888);Canvas bitmapCanvas3 = new Canvas(bitmap3);bitmapCanvas3.drawPath(getPathC(), mCPaint); // YELLOW  Ccanvas.clipPath(getPathC(), Region.Op.INTERSECT); // 指定裁剪区域为C,取和上次的裁剪区域的交集canvas.drawBitmap(bitmap3,0,0,null);}private Path getPathA(){path.reset();path.lineTo(0, viewHeight);path.lineTo(points.get("b").x,points.get("b").y);path.quadTo(points.get("c").x,points.get("c").y, points.get("d").x, points.get("d").y);path.lineTo(a.x, a.y);path.lineTo(points.get("e").x, points.get("e").y);path.quadTo(points.get("g").x,points.get("g").y, points.get("f").x, points.get("f").y);path.lineTo(viewWidth,0);path.close();return path;}/*** 绘制默认的界面* @return*/private Path getPathDefault(){path.reset();path.lineTo(0, viewHeight);path.lineTo(viewWidth,viewHeight);path.lineTo(viewWidth,0);path.close();return path;}private Path getPathB(){path.reset();path.lineTo(0, viewHeight);path.lineTo(points.get("b").x,points.get("b").y);path.quadTo(points.get("c").x,points.get("c").y, points.get("d").x, points.get("d").y);path.lineTo(a.x,a.y);path.lineTo(points.get("e").x, points.get("e").y);path.quadTo(points.get("g").x,points.get("g").y, points.get("f").x, points.get("f").y);path.lineTo(viewWidth,viewHeight);path.lineTo(0, viewHeight);path.close();return path;}/*** 绘制区域C* @return*/private Path getPathC(){path.reset();path.moveTo(points.get("i").x, points.get("i").y);path.lineTo(points.get("d").x, points.get("d").y);path.lineTo(a.x,a.y);path.lineTo(points.get("e").x, points.get("e").y);path.lineTo(points.get("j").x, points.get("j").y);path.close();return path;}}

如果你习惯看PDF的文档,这里握上传到了gitee,地址:https://gitee.com/weizu_cool/Android_TurePage/blob/master/Android%E7%BF%BB%E9%A1%B5%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0.pdf

Android翻页入门相关推荐

  1. Android翻页入门学习(三)阴影绘制

    对于阴影的绘制,首先需要使用的是渐变色的绘制,在Android中,可以使用GradientDrawable对象中的setBounds和draw来进行绘制. 参考文档如下:https://develop ...

  2. Android翻页效果原理实现之翻页的尝试

    炮兵镇楼 在<自定义控件其实很简单>系列的前半部分中我们用了整整六节近两万字两百多张配图讲了Android图形的绘制,虽然篇幅很巨大但仍然只是图形绘制的冰山一角,旨在领大家入门,至于修行成 ...

  3. android翻页实现原理

    Android平台中的二种翻页效果实现. 第一种翻页效果如下: 实现原理: 当前手指触摸点为a,则 a点坐标为(ax,ay), 由三角形acb与三角形cmb为对称三角形并且直线cp为am垂直平分线,则 ...

  4. Android 翻页效果 电子书 (转)

    转载请注明来自: 5进制空间-android区 相信做电子书的同学,都遇到过翻页动画的需求吧,如果你不满足与点击滑动翻页的话,这边文章应该能够帮助到你. 先上个效果图: [img]http://www ...

  5. Android翻页效果原理实现之引入折线

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 尊重原创 ...

  6. android 翻页动态效果,Android 实现翻书的动画效果

    当你们看到这标题时,多少觉得好牛逼哦,其实我想说的是,这只是一个思路,只是简单的三维空间旋转.为什么要写这个博客呢,最近在面试时,面试官突然问这样的效果怎么实现的,当时我说用动画,他说具体点,我说用R ...

  7. android 翻页卷曲效果 电子书翻页

    先上个效果图: 效果还是很不错的,不过与ibook那个效果比起来,还是有差距的.应为这个没用到openGL做3D效果,只是用的2d的canvas画布去画的view,添加了阴影效果,还是挺有立体感的.而 ...

  8. Android翻页效果

    先上个效果图: 效果还是很不错的,不过与ibook那个效果比起来,还是有差距的.应为这个没用到openGL做3D效果,只是用的2d的canvas画布去画的view,添加了阴影效果,还是挺有立体感的.而 ...

  9. Android之实现上下左右翻页效果

    如果实现上下或者左右翻页效果,我们借助下这个开源项目:https://github.com/openaphid/android-flip Aphid FlipView是一个能够实现Flipboard翻 ...

最新文章

  1. 学习总结--团队项目
  2. 【整理】SYSCOMMAND的wParam值的宏定义
  3. FMDB支持的事务类型
  4. nhibernate3 linq的的select 操作
  5. Codeforces Round #656 (Div. 3) F. Removing Leaves 贪心 + 模拟
  6. torch tensor去掉1维_浑身是刺的“维c之王”,有人管它叫“菠萝”,有人管它叫“梨”...
  7. 惊艳!小米折叠屏新机渲染图曝光:不输华为三星
  8. Redis 集群(学习笔记十)
  9. LTE中QPSK、16QAM、64QAM什么意思?有什么区别,分别在什么情况下占用呢?
  10. ectouch微信支付,带微信H5支付
  11. U - 计算长方体、四棱锥的表面积和体积
  12. 天刀 服务器状态,12月11日服务器例行维护公告(已完成)
  13. winrar破解注册
  14. 智能机器人比巴和智伴哪个好_比巴智能早教机器人怎么样 比巴智能早教机器人使用测评...
  15. vs可以开发python吗_vs可以写python吗
  16. Eclipse 字体不一致问题
  17. PL/SQL_高级编程
  18. AVPlayer 多媒体播放器
  19. Android客户端+JavaEE后台服务器端+Mysql数据库(社交app-心情驿站)
  20. 计算机天才楼天城回母校杭十四中

热门文章

  1. Android中控件的显示和隐藏以及EditText的可编辑和不可编辑状态
  2. Docker 中国官方镜像加速
  3. Python取整——向上取整、向下取整、四舍五入取整、向0取整
  4. 坚果云android功能,坚果云-安卓版5大新功能,一次性解锁!
  5. RegexBuddy 使用指南
  6. [附源码]JSP+ssm计算机毕业设计“美丽乡村摄影”网站设计与实现fumie【源码、数据库、LW、部署】
  7. java 回调url_java开发之——[接口回调]
  8. jquery 数组 操作函数
  9. Linux服务器开启ssh服务,实现ssh远程登陆
  10. 把憨豆和蒙娜丽莎P成蒙娜豆沙,网络神经能办到!