支持原创哦。yhGO。
项目中需要一个这样的控件,本来是从网上下了一个写好的仿ToolBar控件,但是一看惨不忍睹,只实现了开始和结束两个状态。中间过程的线条变换杂乱无章。又正好学习完Path之后技痒难耐。嘿嘿。。。就自己试着写了一个。
效果:
就是这个菜单按钮随着侧滑菜单的打开,从横着的三条杠变为一个向左的箭头。
优势:
可以与任意侧滑菜单控件等结合使用。比toolbar的该效果使用起来灵活很多。
原理:
让我再看一下分解图片:
相信大家看过效果图之后,都能明白,这个效果说白了就是对那三条线进行位移旋转变换。而难点在于怎么让这三条线在变幻的时候还能是一个整体,而不是各变各的显得杂乱无章。
好了,废话不说,开始分析:
首先,我们应该再把这个效果简化一些。我们知道两点确定一条直线,所以,我们可以将上面三条线段的变换转化为6个点的变换。
如图所示A,B,C,D,E,F六个点。同时我在图上加了两个辅助圆。点A,B,C,D在圆1上。点E,F在圆2上。圆1,圆2是以点o为圆心的同心圆。半径分别为AO,EO。(ps,图是我手绘的,不太标准,大家懂就好~~)
看到这两个辅助圆,聪明的同学是不是想到什么?教师附身么,啊!!救我!
咳咳,接着我们刚才的思路,将三条线简化为了6个点的变换。那点的变化是什么?点动成线,没错,这两个同心圆就是6个点的变换线路。我们看下一副图:
如图所示,灰色的线段bc,da,和FE所组成的图形就是我们变换后的箭头。
所以我们可得这6个点的变化
A->a,B->b,E->F,F->E,C->c,D->d
还是那句话,点动成线。既然我们不是只要动画的开始点与结束点。那么便要求出A->a,B->b,E->F,F->E,C->c,D->d这六个变化中的所有点。那就是Aa,Bb,EF,FE,Cc,Dd这六条圆弧。
我们用Path类中的这个方法来得到一个圆弧:
public void addArc (RectF oval, float startAngle, float sweepAngle)
Oval:用来确定这个圆(椭圆)半径和圆心的矩形。
startAngle:开始的角度。
sweepAngle:这个圆弧划过的角度。
注:Android中的正角度为顺时针方向。
下面我就以点A为例,求得A->a这一段圆弧。
首先确定oval。也就是内切圆1的正方形。可知,该正方形的宽高等于圆1的直径。聪明的同学这是在内心估计已经开始吐槽了。。绕了一大圈不就是求AO的长嘛。对的!真聪明,哈。我们设线段AB的长为m,初始时三条线段间的宽相等为h。AO为:
flaot AO= Math.sqrt(m*m/4+h*h);
以点O为画布坐标原点。
矩形为:
RectF rect=new RectF(-AO, -AO, AO, AO);
矩形搞定,下面我们求startAngle:
根据上图,我们可以知道startAngle=270-角1;
所以关键在于求角1。而我们知道AN和ON的长(宽和高的一半),于是角1的度数呼之欲出~~~~(三角函数,对比邻)
float angle1=(float)(Math.toDegrees(Math.atan( (AN/2)/(NO/2))));
所以
startAngle=270-angle1;
sweepAngle=180+angle1;(默认aO垂直于EF)
三个变量凑齐,召唤神龙!!!!,哦不,是弧线。恩....
接下来,我们进行下一步。什么?你说还有五条弧线。。。duang,duang,duang,完成!
接下来,就是根据侧滑菜单的打开程度取这五条弧线上响应的点。
用PathMeasue类来完成它。
先将我们的弧线初始化给PahtMeasue。
setPath(一条弧线, false);
接下来核心方法隆重登场:
PathMeasure类中的
boolean getPosTan (float distance, float[] pos, float[] tan)
方法各个参数释义:
参数 | 作用 | 备注 |
返回值(boolean) | 判断获取是否成功 | true表示成功,数据会存入 pos 和 tan 中, |
distance | 距离 Path 起点的长度 | 取值范围: 0 <= distance <= getLength |
pos | 该点的坐标值 | 坐标值: (x==[0], y==[1]) |
tan | 该点的正切值 | 正切值: (x==[0], y==[1]) |
用法很简单,将distance,还有创建好的pos和tan(注意tan不能为null)这三个参数传进去。即可得到在弧线上距离弧线起点距离为distance的点的坐标(pos中存放)。
接下来将求出来的6个点,对应两点一一连线。
完成!!
Demo:
在github上:ToolBarMenuButton