前言#
工作多年,有史以来,第一次尝试在简书和github上开始分享之旅,惭愧之际。为什么选择简书,简洁,大方,得体。为什么首先选择自定义控件这个知识点,因为是我薄弱地方,所以我要重新认识,再加深理解。本人用的小米手机,所以就直接在小米手机上面寻找模仿的UI界面咯。
效果预览#
为了做这个gif动画,还是花了点心思,第一次弄。
第一步:先用手机录屏软件或者其他方式(自行搜索)先录制好mp4文件。
第二步:我用的是mac,推荐在AppStore下载VideoGIF Free这款软件制作,将mp4转成gif。
实现思路#
1、自定义view,首先利用canvas.drawArc不断的画圆弧,通过改变角度值达到从0到有的圆圈。
2、等角度达到一定值的时候,开始显示绘制正确完成的钩子。
3、开始绘制圆弧的时候,一并绘制周围的三角形,这个是随机生成,然后慢慢想最新靠拢。
4、整体绘制完成,显示提示文字。
1、画圆弧,并且不断加角度,让其最后绘制成一个圆。通过postInvalidateDelayed方法让圆弧不停的自我绘制,直到角度的值等于360°算绘制完毕。绘制完毕后,回调调用完成的接口。
/**
* 画圆弧
*/
private void drawArc(Canvas canvas) {
RectF rectF = new RectF();
rectF.left = (getWidth() - arcWidth) / 2;
rectF.top = (getHeight() - arcWidth) / 2;
rectF.right = (getWidth() - arcWidth) / 2 + arcWidth;
rectF.bottom = (getHeight() - arcWidth) / 2 + arcWidth;
if (isDraw) {
//未运行的的时候可以执行一次
if (!isDrawing) {
startBezierAnimotion();
}
isDrawing = true;
canvas.drawArc(rectF, -90, arcAngle, false, mArcPaint);
arcAngle += ARC_CREATE_ANGLE;
if (arcAngle > 330) {
drawRight(canvas);
}
postInvalidateDelayed(ARC_CREATE_TIME); //每隔一段时间开始绘制
if (arcAngle >= 360) {
isDraw = false;
}
drawTriAngle(canvas);
} else {
canvas.drawArc(rectF, -90, 360, false, mArcPaint);
drawRight(canvas);
isDrawing = false;
if(listener != null) {
listener.onAnimotionFinished();
}
}
}
2、绘制钩子。当角度大于330°的时候,绘制钩子,钩子是根据圆弧的相对角度绘制而成的,利用path就可以做到。
/**
* 画正确的钩子
*
* @param canvas\
*/
private void drawRight(Canvas canvas) {
Path path = new Path();
path.moveTo((getWidth() - arcWidth) / 2 + arcWidth / 4 + arcWidth / 16, (getHeight() - arcWidth) / 2 + arcWidth / 2);
path.lineTo((getWidth() - arcWidth) / 2 + arcWidth / 4 + arcWidth / 8 + arcWidth / 16, (getHeight() - arcWidth) / 2 + (arcWidth / 4) * 3 - arcWidth / 8);
path.lineTo((getWidth() - arcWidth) / 2 + arcWidth / 2 + arcWidth / 4 - arcWidth / 8 + arcWidth / 16, (getHeight() - arcWidth) / 2 + arcWidth / 4 + arcWidth / 8);
canvas.drawPath(path, mRightPaint);
}
3、圆弧转动的动的时候,周围生成若干个随机的三角形,这里我生成的是十个。三角形的所处的位置以及运动的轨迹由ValueAnimator动画生成。
/**
* 坐标点的位置,用户传递到属性动画当中
*/
public class Point implements Serializable {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(Point src) {
this.x = src.x;
this.y = src.y;
}
public void set(int x, int y) {
this.x = x;
this.y = y;
}
}
/**
* 用于属性动画的回掉,根据返回的参数t来计算相关数据,t是从0~1变化。
*/
public class BezierEvaluators implements TypeEvaluator<List<Point>> {
private Point controllPoint;
public BezierEvaluators(Point controllPoint) {
this.controllPoint = controllPoint;
}
@Override
public List<Point> evaluate(float t, List<Point> startValue, List<Point> endValue) {
List<Point> list = new ArrayList<>();
if (startValue.size() > 0 && endValue.size() > 0 && startValue.size() == endValue.size()) {
for (int i = 0; i < startValue.size(); i++) {
//增加角度,让每个碎片三角形所处位置不一样
double sum = t + ((double) i / (double) 10);
if (sum >= 1) {
sum = sum - 1;
}
// 200 - (80 * t) 旋转越来越靠近圆
double x = controllPoint.x + (arcWidth / 2 + 200 - (80 * t)) * Math.cos(360 * sum * Math.PI / 180);
double y = controllPoint.y + (arcWidth / 2 + 200 - (80 * t)) * Math.sin(360 * sum * Math.PI / 180);
list.add(new Point((int) x, (int) y));
}
}
return list;
}
}
/**
* 开始运动三角形
*/
private void startBezierAnimotion() {
if(isShowTriAngle == 1) {
float circleX = (getWidth() - arcWidth) / 2 + arcWidth / 2;
float circleY = (getHeight() - arcWidth) / 2 + arcWidth / 2;
float x = ((getWidth() - arcWidth) / 2) / 2;
Point controllPoint = new Point((int) circleX, (int) circleY);
BezierEvaluators bezierEvaluator = new BezierEvaluators(controllPoint);
//初始化开始坐标和结束坐标,在这里这个坐标并使用,只是为了能够得属性动画产生的变化值
List<Point> startPoint = new ArrayList<>();
List<Point> endPoint = new ArrayList<>();
triAngle.clear();
for (int i = 0; i < 10; i++) {
startPoint.add(new Point(i, i)); //随意生成
endPoint.add(new Point(i, i)); //随意生成
//半径之外的范围 150 - 200之间
double r = Math.random() * 30 + 20;
triAngle.add((int) r);
}
ValueAnimator anim = ValueAnimator.ofObject(bezierEvaluator, startPoint, endPoint);
anim.addUpdateListener(this);
anim.setDuration(1500);
anim.setInterpolator(new AccelerateDecelerateInterpolator()); //动画运动的速率,开始和结束慢,中间快,还有其他方式可设置。
anim.start();
}
}
/**
* 回调过来的数据,开始更新,画三角形,这样子就形成了三角形围着圆弧转,并且越靠越近,最后消失。
*/
@Override
public void onAnimationUpdate(ValueAnimator animation) {
List<Point> valuePoints = (List<Point>) animation.getAnimatedValue();
this.valuePoints = valuePoints;
invalidate();
}
4、动画执行完毕之后,通知接口已完成。
canvas.drawArc(rectF, -90, 360, false, mArcPaint);
drawRight(canvas);
isDrawing = false;
if(listener != null) {
listener.onAnimotionFinished();
}
结语#
到这里,大致流程就介绍完了。有什么不足的,欢迎指出。
欢迎在下方评论和喜欢,谢谢,转载请说明出处。
欢迎在Github上Star,详细的DEMO地址在https://github.com/yangxixi88/ProcessSuccessView