前几篇中我们学习过了一些规则及不规则图形的绘制技巧,不知道大家理解是否到位,这篇文章中将继续前文主题,研究Path
的基本应用,前文中我们已经基本了解了Path
的构建,接下来我们来学习一种比较炫酷的动画方案-路径动画,路径动画指的是某一图形或者控件按照固有路径进行绘制或者运动。下面展示的就是一个典型的路径动画:
下面我们一起学习下如何实现该动画。
形成过程
按照惯有思路,我们观察动图,分析动画的构成,可以得到下图:
六边形
两个六边形的绘制方法相信小伙伴们并不陌生吧,我们在第⑤篇中已经有基本思路了,基于已有思路我们可以建立下图坐标系
同时那么我们可以得到线条运动轨迹为:
O-A-B-C-D-E-O-F-G-H-J-J-O
假设坐标原点O的坐标为:
O(mCenterX,mCenterY)
两个六边形的外接圆半径为:
mRadius
我们可以得到左边六边形外接圆圆心坐标为:
a = mCenterX - mRadius;
b = mCenterY
左边六边形圆上点坐标为:
x = a + mRadius * (float) Math.cos(Math.PI * M/ 180)
y = b + mRadius * (float) Math.sin(Math.PI * M/ 180)
(M为角度值,顺时针为正值,逆时针为负值)
同理我们可以得到右侧六边形的外接圆心坐标及圆上点坐标公式:
a = mCenterX + mRadius;
b = mCenterY ;x = a + mRadius * (float) Math.cos(Math.PI * M/ 180)
y = b + mRadius * (float) Math.sin(Math.PI * M/ 180)
(M为角度值,顺时针为正值,逆时针为负值)
线条
移动的线条本质是获取了运动轨迹:
O-A-B-C-D-E-O-F-G-H-J-J-O
中的一部分,然后通过改变这部分在轨迹上的位置而形成的。
构造六边形Path
根据形成过程中的分析,我们可以构造两个六边形的Path
对象如下:
mPath.reset();
//移动到View中心
mPath.moveTo(mCenterX, mCenterY);
//左侧六边形
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 60 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 60 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 120 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 120 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 180 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 180 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 240 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 240 / 180));
mPath.lineTo(mCenterX - mRadius + mRadius * (float) Math.cos(Math.PI * 300 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 300 / 180));
mPath.lineTo(mCenterX, mCenterY);
//右侧六边形
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 120 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 120 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 60 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 60 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 0 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 0 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 300 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 300 / 180));
mPath.lineTo(mCenterX + mRadius + mRadius * (float) Math.cos(Math.PI * 240 / 180),
mCenterY + mRadius * (float) Math.sin(Math.PI * 240 / 180));
mPath.lineTo(mCenterX, mCenterY);
运行后可以看到下图就证明你已迈出了长征的一半。
构造线条Path
如形成过程中分析,移动的线条本质上来源于六边形所组成的运动轨迹中的一段,在构造六边形Path
中我们已经获得了运动轨迹,接下来就是从运动轨迹中获取一段固定距离了,那么那个类能实现这个功能呢?PathMeasure
(主角上场了)
在使用PathMeasure
类时,需要为其设置一个Path
对象,可以通过构造传入,也可以通过setPath
方法设置,PathMeasure
类提供了如下公有方法:
查看函数说明我们需要使用的自然是getSegment()
函数,说明如下(其他方法后续文章中介绍):
/**
* startD 开始点距离起点的距离
* stopD 结束位置距离起点的位置
* dest 截取到的Path对象
**/
getSegment(float startD,float stopD,Path dest,boolean startWithMoveTo)
假设线条长度为20,那么我们从Path中截取刚开始的一段距离的代码如下:
mPathMeasure = new PathMeasure(mPath,false);
float length = mPathMeasure.getLength();
Path linePath = new Path();
linePath.reset();
//需要调用该方法重置Path,否则获取到的Path段不能被绘制出来
linePath.rLineTo(0,0);
mPathMeasure.getSegment(0,20,linePath,true);
canvas.drawPath(linePath,mLinePaint);
运行后效果:
动画
已经绘制出来基本图形,那么线条动起来就简单了,直接控制getSegment
函数的startD
参数变化就可以了。代码如下:
public void startAnimation(){
mValueAnimator = ValueAnimator.ofFloat(0,1.0f);
mValueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentValue = (float) animation.getAnimatedValue();
postInvalidate();
}
});
mValueAnimator.setDuration(3000);
mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
mValueAnimator.setRepeatCount(-1);
mValueAnimator.start();
}
float length = mPathMeasure.getLength();
Path linePath = new Path();
linePath.reset();
linePath.rLineTo(0,0);
mPathMeasure.getSegment(length*mCurrentValue,length*mCurrentValue+mLineDistance,linePath,true);
canvas.drawPath(linePath,mLinePaint);
到此,我们就完成了上面动画的绘制,不知道你们get到没,查看完整代码,请在后台回复2即可