效果
首先看一下效果
实现思路
仔细看这个效果,将整个过程一个一个的分解:
分解
1.画一个箭头
2.箭头渐变
3.弹出圆球
4.画圆弧
5.画勾
每个过程通过一个进度百分比来记录进度,然后就是按顺序一个个的画出来
public class LoadingView extends View {
// 开始绘制的标志
private boolean mCanDraw = true;
private boolean mIsStart = true;
private int mDrawUpLinePercent;
private int mDrawOnLinePercent;
private int mOutPointPercent;
private int mDrawGouPercent;
private int mWidth = 0;
private int mheight = 0;
// 圆心半径
private float mCircleX = 0;
private float mCircleY = 0;
private float mRedius = 80;
private int mOuterCircleStrokeWidth = 10;
private Paint mPaint;
private RectF mRectF;
public LoadingView(Context context) {
this(context, null);
}
public LoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mOuterCircleStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.parseColor("#44FFFFFF"));
}
@Override
protected void onDraw(Canvas canvas) {
// 获取圆心
mCircleX = mWidth / 2;
mCircleY = mheight / 2;
// 这个path一定只能在这里初始化
Path path = new Path();
mRectF = new RectF(mCircleX - mRedius, mCircleY - mRedius, mCircleX + mRedius, mCircleY + mRedius);
// 绘制外圆
mPaint.setColor(Color.parseColor("#44FFFFFF"));
canvas.drawCircle(mCircleX, mCircleX, mRedius, mPaint);
if (mCanDraw) {
mIsStart = true;
mPaint.setColor(Color.WHITE);
if (mDrawUpLinePercent < 95) {
// 绘制竖线
float temp = (mCircleY / 4) * mDrawUpLinePercent / 100;
canvas.drawLine(mCircleX, mCircleY * 3 / 4 + temp,
mCircleX, mCircleY * 5 / 4 - temp, mPaint);
// 竖线变化的时候 箭头一直存在
path.moveTo(mCircleX * 3 / 4, mCircleY);
path.lineTo(mCircleX, mCircleY * 5 / 4);
path.lineTo(mCircleX * 5 / 4, mCircleY);
canvas.drawPath(path, mPaint);
mDrawUpLinePercent += 5;
} else {
// path变成直线
if (mDrawOnLinePercent < 100) {
// 箭头变形
path.moveTo(mCircleX * 3 / 4, mCircleY);
float temp = (mCircleY / 4) * mDrawOnLinePercent / 100;
path.lineTo(mCircleX, mCircleY * 5 / 4 - temp);
path.lineTo(mCircleX * 5 / 4, mCircleY);
canvas.drawPath(path, mPaint);
mDrawOnLinePercent += 5;
// 圆心一直存在
canvas.drawCircle(mCircleX, mCircleY, 2.5f, mPaint);
} else {
// 将点弹出圆外
if (mOutPointPercent < 100) {
float temp = mRedius * mOutPointPercent / 100;
canvas.drawCircle(mCircleX, mCircleY - temp, 2.5f, mPaint);
mOutPointPercent += 5;
//弹出圆外的过程中,直线是一直存在的
canvas.drawLine(mCircleX * 3 / 4, mCircleY, mCircleX * 5 / 4, mCircleY, mPaint);
mOutPointPercent += 5;
} else {
if (mDrawGouPercent < 100) {
// 同时画勾
path.moveTo(mCircleX * 3 / 4, mCircleY);
float t = (mCircleY / 4) * mDrawGouPercent / 100;
path.lineTo(mCircleX, mCircleY + t);
path.lineTo(mCircleX * 5 / 4, mCircleY);
canvas.drawPath(path, mPaint);
// 画圆弧
mPaint.setStrokeWidth(mOuterCircleStrokeWidth);
float temp = (mDrawGouPercent / 100f) * 360;
canvas.drawArc(mRectF, 270, -temp, false, mPaint);
mDrawGouPercent += 5;
} else {
// 绘制最终的圆
mPaint.setColor(Color.WHITE);
canvas.drawCircle(mCircleX, mCircleY, mRedius, mPaint);
// 绘制最终的勾
path.moveTo(mCircleX * 3 / 4, mCircleY);
path.lineTo(mCircleX, mCircleY * 5 / 4);
path.lineTo(mCircleX * 5 / 4, mCircleY * 4 / 5);
canvas.drawPath(path, mPaint);
// 绘制完成
mCanDraw = false;
}
}
}
}
} else {
// 绘制最终的圆
mPaint.setColor(Color.WHITE);
canvas.drawCircle(mCircleX, mCircleY, mRedius, mPaint);
// 绘制最终的勾
path.moveTo(mCircleX * 3 / 4, mCircleY);
path.lineTo(mCircleX, mCircleY * 5 / 4);
path.lineTo(mCircleX * 5 / 4, mCircleY * 4 / 5);
canvas.drawPath(path, mPaint);
mIsStart = false;
// 绘制静态箭头
// canvas.drawLine(mCircleX, mCircleY * 3 / 4,
// mCircleX, mCircleY * 5 / 4, mPaint);
//
// path.moveTo(mCircleX * 3 / 4, mCircleY);
// path.lineTo(mCircleX, mCircleY * 5 / 4);
// path.lineTo(mCircleX * 5 / 4, mCircleY);
// canvas.drawPath(path, mPaint);
}
if (mIsStart) {
postInvalidateDelayed(30);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
mWidth = width;
} else {
mWidth = 400;
}
if (heightMode == MeasureSpec.EXACTLY) {
mheight = height;
} else {
mheight = 400;
}
// 设置自己的真实大小
setMeasuredDimension(width, height);
}
}
踩的坑
这个view我自己调了一下午才调出来,还几次都想放弃,最后还是坚持下来了,给自己点个赞!下面说说我遇到的问题吧:
1.坐标计算不熟悉,花费了很多时间,动手比较少
2.这个进度onDraw每次触发的时候,我们希望重新绘制下个画面,然而我在画直线的时候将new Path()放在了全局,相当于每次对这个path对象操作,导致每次onDraw画的直线都在,花了好久才找到原因!
3.Paint的一些属性改变之后需要重新设置.
本文介绍了一个自定义的加载动画视图实现方法,详细解释了如何使用Android Canvas和Path API来逐步绘制出复杂的动画效果,包括箭头渐变、弹出圆球等。

被折叠的 条评论
为什么被折叠?



