自定义View实现圆环环绕的加载动画
发布日期:2021-06-29 11:46:23 浏览次数:3 分类:技术文章

本文共 5779 字,大约阅读时间需要 19 分钟。

先看效果图:

这是我在某软件上看到的加载动画,感觉挺不错,就自己研究了一下。下面给大家分享一下该动画的实现过程

一、三个圆环的绘制和运动分析

来看下面这张解析图:

假设每个圆环的初始位置如上图,那么我们可以设定每一个圆环的位置以及它离控件边界的距离(w/6)

为了方便,我们定义控件宽度getWidth()为w,那么左上,右上,正下方圆环的圆心坐标依次为:

(w/4, w/4),(w*3/4, w/4),(w/2, w*3/4)

我们再来看下面这个动画:

发现了吧,实际上每个圆环都在做直线运动,三个圆环组合在一起就呈现出了相互靠近和远离的效果。

所以我们的最终目的是让这三个圆环延三条线段做匀速运动,当某一个圆环移动到了线段的端点时,就要改变它的运动状态,让它延另一条线段移动。

我们作图解析:

梳理一下:

  1. 处在左上角的圆环会延着编号1的线段移动,右上角的圆环会延着编号2的线段移动,正下方的圆环会延着编号3的线段移动。
  2. 彼此到达所在线段端点时(保证到达线段端点耗费的时间都相同),改变自身的运动状态,延着下一条线段继续运动。

二、圆环绘制和运动的代码解释

我们先创建一个MovePoint类,保存每个圆环的圆心坐标,所在直线的斜率与截距和每次移动的步长:

public class MovePoint {    private float x;    private float y;    private float k,b;      //直线函数式中的斜率与截距    private float moveStep;   //移动步长    public float getX() {        return x;    }    public void setX(float x) {        this.x = x;        setY(x*k+b);    }    public float getY() {        return y;    }    public void setY(float y) {        this.y = y;    }    public void setCalculate(float k,float b){        this.k=k;        this.b=b;    }    public float getK() {        return k;    }    public void setK(float k) {        this.k = k;    }    public float getB() {        return b;    }    public void setB(float b) {        this.b = b;    }    public float getMoveStep() {        return moveStep;    }    public void setMoveStep(float moveStep) {        this.moveStep = moveStep;    }}

初始化各属性:

private Paint circlePaint;private int circleRadius=0; //圆的半径private MovePoint[] movePoints=new MovePoint[3];    //记录三个圆环的位置,状态,和移动时的函数公式private float[] x;    //三个圆环的x坐标private float[] k,b; //三个圆环所在直线的斜率与截距private int moveTime=70;  //移动到线段端点需要的时间public MyThreeCircleLoadView(Context context, @Nullable AttributeSet attrs) {    super(context, attrs);    init();}private void init(){    circlePaint=new Paint();    circlePaint.setColor(Color.RED);    circlePaint.setStyle(Paint.Style.STROKE);    circlePaint.setAntiAlias(true);}/** 在左上角向右边移动; y=getWidth()/4* 在右上角向左下移动; y=-2*x+getWidth()*7/4* 在正下方向左上移动; y=2*x-getWidth()/4* */private void setMovePoints(){    x=new float[]{getWidth()/4.0f,getWidth()*3/4.0f,getWidth()/2.0f};    k=new float[]{0,-2,2};b=new float[]{getWidth()/4.0f,getWidth()*7/4.0f,-getWidth()/4.0f};    int len=movePoints.length;    for(int i=0;i

注意移动步长的计算,因为我这里每次改变的是每个圆环的x坐标,而不是实际的线段距离,所以在getMoveStep()中,只需要(x1-x2)获得线段端点间的水平距离。

我们要求每个圆环到达线段端点的时间相等,由匀速运动公式可得v=(x/t),我们给定相同的t,x又是已知的,就可以得到每个圆环在当前线段上的速度v。

每个点都应该用float变量,否则会产生速度计算上的误差

 绘制圆环:

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    for (MovePoint movePoint : movePoints) {        canvas.drawCircle(movePoint.getX(), movePoint.getY(), circleRadius, circlePaint);        changeCircleState(movePoint);    }    move();}//改变圆环移动的状态private void changeCircleState(MovePoint movePoint){    //从左上角向右移并移动到右上方顶点时    if (movePoint.getX() >= x[1] && movePoint.getY() == movePoint.getX() * k[0] + b[0]) {        movePoint.setCalculate(k[1], b[1]);        movePoint.setMoveStep(getMoveStep(x[2], x[1], moveTime));    }    //右上向坐下移动并移动到正下方顶点时    else if (movePoint.getX() <= x[2] && movePoint.getY() == movePoint.getX() * k[1] + b[1]) {        movePoint.setCalculate(k[2], b[2]);        movePoint.setMoveStep(getMoveStep(x[0], x[2], moveTime));    }    //从正下方向左上移动并移动到左上方顶点时    else if (movePoint.getX() <= x[0]) {        movePoint.setCalculate(k[0], b[0]);        movePoint.setMoveStep(getMoveStep(x[1], x[0], moveTime));    }}

changeCircleState():用于判断圆环是否运动到线段的端点。若运动到了端点,就改变该圆环的运动状态和附属的线段

 移动圆环:

//移动圆环private void move(){    for (MovePoint movePoint : movePoints) {        movePoint.setX(movePoint.getX() + movePoint.getMoveStep());    }    postInvalidateDelayed(5);}

下面是自定义View的完整代码:

import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.util.Log;import android.view.View;import com.bean.MovePoint;import androidx.annotation.Nullable;public class MyThreeCircleLoadView extends View {    private Paint circlePaint;    private int circleRadius=0; //圆的半径    private MovePoint[] movePoints=new MovePoint[3];    //记录三个圆环的位置,状态,和移动时的函数公式    private float[] x;    //三个圆环的x坐标    private float[] k,b; //三个圆环所在直线的斜率与截距    private int moveTime=70;  //移动到线段端点需要的时间    public MyThreeCircleLoadView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    private void init(){        circlePaint=new Paint();        circlePaint.setColor(Color.RED);        circlePaint.setStyle(Paint.Style.STROKE);        circlePaint.setAntiAlias(true);    }    /*    * 在左上角向右边移动; y=getWidth()/4    * 在右上角向左下移动; y=-2*x+getWidth()*7/4    * 在正下方向左上移动; y=2*x-getWidth()/4    * */    private void setMovePoints(){        x=new float[]{getWidth()/4.0f,getWidth()*3/4.0f,getWidth()/2.0f};        k=new float[]{0,-2,2};b=new float[]{getWidth()/4.0f,getWidth()*7/4.0f,-getWidth()/4.0f};        int len=movePoints.length;        for(int i=0;i
= x[1] && movePoint.getY() == movePoint.getX() * k[0] + b[0]) { movePoint.setCalculate(k[1], b[1]); movePoint.setMoveStep(getMoveStep(x[2], x[1], moveTime)); } //右上向坐下移动并移动到正下方顶点时 else if (movePoint.getX() <= x[2] && movePoint.getY() == movePoint.getX() * k[1] + b[1]) { movePoint.setCalculate(k[2], b[2]); movePoint.setMoveStep(getMoveStep(x[0], x[2], moveTime)); } //从正下方向左上移动并移动到左上方顶点时 else if (movePoint.getX() <= x[0]) { movePoint.setCalculate(k[0], b[0]); movePoint.setMoveStep(getMoveStep(x[1], x[0], moveTime)); } } //移动圆环 private void move(){ for (MovePoint movePoint : movePoints) { movePoint.setX(movePoint.getX() + movePoint.getMoveStep()); } postInvalidateDelayed(5); }}

xml布局文件:

 

转载地址:https://blog.csdn.net/zz51233273/article/details/108198230 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android 自定义View利用Path实现变速圆周运动的环绕加载动画
下一篇:自定义View实现注销图案的加载动画

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月09日 11时11分05秒