最近项目要求做一个进度条,头部有一个龟头小圆,如下:
拿到图第一时间就是对整个图片进行技术拆分,由内到外可分为5个部分:
1.绘制实心圆
Paint pain=new Paint;
pain.setStyle(Paint.Style.FILL);
addCircle(float x, float y, float radius, @NonNull Direction dir)
2.绘制二阶贝塞尔曲线
quadTo(float x1, float y1, float x2, float y2)
3.根据4的轨迹通过PathMeasure 截取 片段
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
4.绘制空心圆
Paint pain=new Paint;
pain.setStyle(Paint.Style.STROKE);
addCircle(float x, float y, float radius, @NonNull Direction dir)
5.一个外圈空心圆包裹一个内圈实心圆
pathMeasure.getPosTan
1和2部分下一篇博客加上,因为涉及贝塞尔曲线
好了, 来看看3-5核心代码。
private void initView(Context context, AttributeSet attrs) {
//内圈画笔
insidePaint = new Paint();
insidePaint.setAntiAlias(true);//去锯齿
insidePaint.setStyle(Paint.Style.STROKE);
insidePaint.setStrokeWidth(insideStrokeWidth);
insidePaint.setColor(Color.WHITE);
//外圈画笔
outsidePaint = new Paint();
outsidePaint.setAntiAlias(true);//去锯齿
outsidePaint.setStyle(Paint.Style.STROKE);
outsidePaint.setStrokeWidth(outSideStrokeWidth);
outsidePaint.setColor(Color.WHITE);
//小龟头空心圆圈画笔
smallPaint = new Paint();
smallPaint.setAntiAlias(true);
smallPaint.setStyle(Paint.Style.STROKE);
smallPaint.setStrokeWidth(smallStrokeWidth);
smallPaint.setColor(Color.WHITE);
//小龟头内圈实体圆圈画笔
smallInsidePaint = new Paint();
smallInsidePaint.setAntiAlias(false);
smallInsidePaint.setStyle(Paint.Style.FILL);
smallInsidePaint.setColor(getResources().getColor(R.color.color_5FBBEB));
}
大致是初始化了4个画笔,从上到下分别负责:4-3-5
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
outSidePath = new Path();
insidePath = new Path();
insidePath.addCircle((w) / 2f + insideStrokeWidth
, (h) / 2f + insideStrokeWidth
, (h) / 2f - 2 * insideStrokeWidth - outSideStrokeWidth
, Path.Direction.CW);
pathMeasure.setPath(insidePath, true);
}
在 sizeChanged阶段 初始化 外圈 内圈 的路径Path,并且初始化内圈半径参数
核心部分
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
outSidePath.reset();
float length = pathMeasure.getLength();
canvas.drawPath(insidePath, insidePaint);
float startD = 0;
float stopD = (Math.abs(progress)) / 100f * length;
pathMeasure.getSegment(startD, stopD, outSidePath, true);
if (progress != 1) {
canvas.drawPath(outSidePath, outsidePaint);
}
pathMeasure.getPosTan(stopD + 2 + outSideStrokeWidth, pos, tan);
Log.d(TAG, "THE POS IS:" + pos[0] + "tan:" + tan[0] + "...stopD:" + stopD);
if (progress != 100 && progress != 1) {
//绘制龟头外圈
canvas.drawCircle(pos[0], pos[1], outSideRadius, smallPaint);
//绘制龟头外圈
canvas.drawCircle(pos[0], pos[1], (outSideRadius - smallStrokeWidth / 2), smallInsidePaint);
}
}
绘制阶段大部分都是计算:有截取片段的开始startD 、stopD和 求曲线路径当前正切坐标值getPosTan
通过外部传进来的Progress 进度控制 截取长度,进而达到类似progress 一直在动的效果。
最后得到初步效果如下:
ps:下方黑色的动效为:Android 自定义View 之 Path PathMeasure (一)
刚开始写博客,格式不是很好,凑合着看吧,有需要的可以私信 ~