前言
最近在学习自定义自定义View相关知识,所以除了学习相关知识外,还要上手练习一些实例。 废话不多说: 先上原设计图

再来一张实现效果图

emmmm,好像不是完全一样啊。大哥,我是模仿,细节问题就不要考虑了吧。
分析阶段
在此之前先复习两个PathMeasure的方法:
-
getPosTan(float distance, float pos[],float tan[]) - path 为 null ,返回 false
-
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 截取path片段,传入起点和终点
首先拿到设计图,分析一波:
5个小球,bulingbuling的发光。然后摆动起来,边摆边画线。
-
5个小球----canvas.drawCircle方法即可实现,没难度。
-
bulingbuling的发光---使用画笔的setShadowLayer属性可以让画出的线变的有发亮的效果。 具体使用如下:
public void setShadowLayer (float radius, float dx, float dy, int color)
radius:阴影半径
dx:X轴方向的偏移量
dy:Y轴方向的偏移量
color:阴影颜色
要想bulingbuling,就要使用动画让阴影半径变化起来。 所以可以设置一个动画来绘制这个效果。
- 5个小球的摆动并绘线。
使用Path绘制5个弧线,同时使用pathMeasure测量path的位置。然后绘制圆球中心为path的位置。这样就能摆动起来。 同时为了绘线,pathMeasure得到某一长度的path绘制出来。
代码实现
创建枚举,控制动画状态:
public enum State {
START, STATE1, STATE2, STATE3, STATE4, STATE5, END
}
相关初始化:
private void init() {
paint1 = new Paint();
paint1.setAntiAlias(true);
paint1.setColor(0xff1aadc0);
paint2 = new Paint();
paint2.setColor(0xff1aadc0);
paint2.setStrokeWidth(40);
paint2.setAntiAlias(true);
paint2.setStyle(Paint.Style.STROKE);
paint2.setStrokeCap(Paint.Cap.ROUND);//笔尖
pathMeasure = new PathMeasure();
initAnimator();
initHandler();
}
Handler和Animator的初始化都很简单,这里不再描述,详情可以看代码。
核心部分
接下来就是核心绘图部分,具体看注释:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(getWidth() / 2, getHeight() / 2); //坐标原点到View中心
int spaceCircle = 30; //设置球之间的间隔
canvas.drawColor(0xff000000); //View背景绘制 可以去掉
paint1.setShadowLayer(30 * mBulingValue, 0, 0, colors[0]); //动画设置path发光半径,bulingbuling
paint2.setShadowLayer(30 * mBulingValue, 0, 0, colors[0]);
switch (mCurrentState) {
case START:
for (int i = 0; i < 5; i++) {
paint1.setStyle(Paint.Style.FILL); //画笔样式
canvas.drawCircle(0, -(spaceCircle + 40) * i, 20, paint1);
}
break;
case STATE1:
paths.clear();
// 5条path计算出来
for (int i = 0; i < 5; i++) {
Path path = new Path();
RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);
path.addArc(oval, -90, 60);
paths.add(path);
}
// 5个小球绘制,并且通过pathMeasure.getPosTan 获得path位置 摆动起来
for (int i = 0; i < 5; i++) {
pathMeasure.setPath(paths.get(i), false);
paint1.setStyle(Paint.Style.FILL);
float[] pos = new float[2];
pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);//获得当前path位置
canvas.drawCircle(pos[0], pos[1], 20, paint1);
}
break;
case STATE2:
paths.clear();
// 这里 同样计算5条path,但是要绘制出第一条,因为第一条已经画好了 下面类似
for (int i = 0; i < 5; i++) {
Path path = new Path();
RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i);
path.addArc(oval, -30, -120);
paths.add(path);
if (i < 1) {
canvas.drawPath(path, paint2);
}
}
//去掉一个点不画,已经用path代替了
for (int i = 0; i < 5; i++) {
if (i > 0 || i == 0) {
pathMeasure.setPath(paths.get(i), false);
paint1.setStyle(Paint.Style.FILL);
float[] pos = new float[2];
pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
canvas.drawCircle(pos[0], pos[1], 20, paint1);
}
}
pathMeasure.setPath(paths.get(1), false);
Path pathDraw2 = new Path();
pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw2, true);
canvas.drawPath(pathDraw2, paint2);
break;
case STATE3:
paths.clear();
for (int i = 0; i < 5; i++) {
Path path = new Path();
RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i); // 外圈
path.addArc(oval, -150, 120);
paths.add(path);
if (i < 2) {
canvas.drawPath(path, paint2);
}
}
for (int i = 0; i < 5; i++) {
if (i > 1 || i == 0) {
pathMeasure.setPath(paths.get(i), false);
paint1.setStyle(Paint.Style.FILL);
float[] pos = new float[2];
pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
canvas.drawCircle(pos[0], pos[1], 20, paint1);
}
}
pathMeasure.setPath(paths.get(2), false);
Path pathDraw3 = new Path();
pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw3, true);
canvas.drawPath(pathDraw3, paint2);
break;
case STATE4:
paths.clear();
for (int i = 0; i < 5; i++) {
Path path = new Path();
RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i); // 外圈
path.addArc(oval, -30, -120);
paths.add(path);
if (i < 3) {
canvas.drawPath(path, paint2);
}
}
for (int i = 0; i < 5; i++) {
if (i > 2 || i == 0) {
pathMeasure.setPath(paths.get(i), false);
paint1.setStyle(Paint.Style.FILL);
float[] pos = new float[2];
pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
canvas.drawCircle(pos[0], pos[1], 20, paint1);
}
}
pathMeasure.setPath(paths.get(3), false);
Path pathDraw4 = new Path();
pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw4, true);
canvas.drawPath(pathDraw4, paint2);
break;
case STATE5:
paths.clear();
for (int i = 0; i < 5; i++) {
Path path = new Path();
RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i); // 外圈
path.addArc(oval, -150, 120);
paths.add(path);
if (i < 4) {
canvas.drawPath(path, paint2);
}
}
for (int i = 0; i < 5; i++) {
if (i > 3 || i == 0) {
pathMeasure.setPath(paths.get(i), false);
paint1.setStyle(Paint.Style.FILL);
float[] pos = new float[2];
pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
canvas.drawCircle(pos[0], pos[1], 20, paint1);
}
}
pathMeasure.setPath(paths.get(4), false);
Path pathDraw5 = new Path();
pathMeasure.getSegment(0, pathMeasure.getLength() * mAnimatorValue, pathDraw5, true);
canvas.drawPath(pathDraw5, paint2);
break;
case END:
paths.clear();
for (int i = 0; i < 5; i++) {
Path path = new Path();
RectF oval = new RectF(-(40 + spaceCircle) * i, -(40 + spaceCircle) * i, (40 + spaceCircle) * i, (40 + spaceCircle) * i); // 外圈
path.addArc(oval, -150, 120);
paths.add(path);
if (i < 5) {
canvas.drawPath(path, paint2);
}
}
pathMeasure.setPath(paths.get(0), false);
paint1.setStyle(Paint.Style.FILL);
float[] pos = new float[2];
pathMeasure.getPosTan(pathMeasure.getLength() * mAnimatorValue, pos, null);
canvas.drawCircle(pos[0], pos[1], 20, paint1);
break;
}
}
值得注意的是:除了第一个点(因为第一个点就是path,没有运动),其他的4个每绘制出一条path后,都要减少一个球的绘制。因为path代替了球。 所以需要判断控制球和path的数量。
项目地址
最后附上github----项目地址,如果喜欢。欢迎fork和start。