android仿视频加载旋转小球动画

    想要知道关于更多自定义View的实例,请参考:android自定义View索引

先上个效果图,以免大家跑错地了。


    嗯,除了只能录三秒,其他没啥问题。

   下面分析一下怎么实现上面这个效果。

    理性分析后我们可以看到是几个小球绕着一个圆进行运动,那这里面的重点我们看看什么。

    绘制五个球,没什么难度,让球绕圆进行运动,这个好像我们没有见到是怎么去实现了,那下就说这个。

    从本质上看,球绕圆运动,其实我们可以看作是一个物体绕指定的路劲运动,那我们就有下面几个东西需要说一下:

1:Path
2:ValueAnimator
3:PathMeasure
复制代码

    前两个大家应该都见过,一个是路径,就是可以自己绘制路线的一个工具,一个是动画,用来指定物体运动的工具,那第三个是一个关于测量路径的类。

    下面说说PathMeasure的用法。

    首先是初始化:

pathMeasure = new PathMeasure(path, false);复制代码

    两个参数第一个,第一个就是我们需要用到的路径,第二个参数意思就是这个以路径头尾是否相连来计算结果,通常我们就写false就行,不会有问题。

    然后是用法:

private float[] mCurrentPositionOne = new float[2];复制代码
float value = (Float) animation.getAnimatedValue();
pathMeasure.getPosTan(value, mCurrentPositionOne, null);复制代码

    我们可以看见把一个二维数组放到了getPosTan这个方法里面,然后还有一个animation,这里的animation来自哪里呢?来自这里:

valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        // 获取当前点坐标封装到mCurrentPosition
        float value = (Float) animation.getAnimatedValue();
        pathMeasure.getPosTan(value, mCurrentPositionOne, null);
        postInvalidate();
    }
});复制代码

    看见没,是动画的监听里面来的,getPosTan的最后一个参数通常也就写null就行了,那么这整个一行的代码意思就是当动画发生了变化,就执行这行代码,然后这行代码会把这个时间点的路径上的坐标赋值给mCurrentPositionOne。

    那我们获取到看这个路径上的坐标点怎么办呢?

    立马用来ondraw里面啊,我的小球此时就可以根据这个坐标点去绘制自己的位置,这个的话,当动画开始时,小球就会不断接受新的坐标,然后不断重绘,最终产生旋转小球的效果。

     我先把属性动画的代码贴出来:

if (valueAnimatorOne == null) {
    valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength());
    valueAnimatorOne.setDuration(800);
    // 减速插值器
    valueAnimatorOne.setInterpolator(new DecelerateInterpolator());
    valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // 获取当前点坐标封装到mCurrentPosition
            float value = (Float) animation.getAnimatedValue();
            pathMeasure.getPosTan(value, mCurrentPositionOne, null);
            postInvalidate();
        }
    });
    valueAnimatorOne.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {
            finishAnimateOne = 1;
        }

        @Override
        public void onAnimationEnd(Animator animator) {
            finishAnimateOne = 0;
        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
}
valueAnimatorOne.start();复制代码

    我写了个800,也就是动画的维持时间,但是我们发现有啊后几个小球,所以我们需要绘制好几个小球,然后给他们不同的动画,为什么呢?因为动画都一样,小球就叠加在一起了,我们就只能看见一个球了。

    说到这里的话,我们的目标算时完成了,具体的操作,大家参考以下代码,或者去:

android自定义View索引

     里面动画的demo进行下载,大家随意,下面给出代码:

/**
 * 仿视频加载动画,旋转的蓝色小球
 */

public class RotaryBall extends View {

    private Path rotationPath;
    private float radius;
    private Paint circlePaintOne;
    private PathMeasure pathMeasure;
    private int finishAnimateOne = 0;   // 用来判断当前动画有没有开始
    private int finishAnimateTwo = 0;   // 用来判断当前动画有没有开始
    private int finishAnimateThree = 0;   // 用来判断当前动画有没有开始
    private int finishAnimateFour = 0;   // 用来判断当前动画有没有开始
    private int finishAnimateFive = 0;   // 用来判断当前动画有没有开始
    private Handler handler;
    private float[] mCurrentPositionOne = new float[2];
    private float[] mCurrentPositionTwo = new float[2];
    private float[] mCurrentPositionThree = new float[2];
    private float[] mCurrentPositionFour = new float[2];
    private float[] mCurrentPositionFive = new float[2];
    private ValueAnimator valueAnimatorOne = null;
    private ValueAnimator valueAnimatorTwo = null;
    private ValueAnimator valueAnimatorThree = null;
    private ValueAnimator valueAnimatorFour = null;
    private ValueAnimator valueAnimatorFive = null;
    private int currentStatus = -1;    //-1表示第一次运行,0表示动画结束或者没开始,1表示正在运动中
    private boolean animateOrNot = true;    //用来决定是否开启动画

    public RotaryBall(Context context) {
        super(context);
        initData();
    }


    public RotaryBall(Context context, AttributeSet attrs) {
        super(context, attrs);
        initData();
    }

    private void initData() {
        rotationPath = new Path();
        circlePaintOne = new Paint();
        circlePaintOne.setColor(Color.BLUE);
        circlePaintOne.setAntiAlias(true);
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 4:
                        if (finishAnimateOne == 0) {
                            startAnimatorOne();
                        }
                        if (finishAnimateTwo == 0) {
                            startAnimatorTwo();
                        }
                        if (finishAnimateThree == 0) {
                            startAnimatorThree();
                        }
                        if (finishAnimateFour == 0) {
                            startAnimatorFour();
                        }
                        if (finishAnimateFive == 0) {
                            startAnimatorFive();
                        }
                        currentStatus = 0;
                }
            }
        };
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        radius = getMeasuredWidth() / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//        rotationPath.addCircle(radius, radius, radius - 10, CW);
        rotationPath.moveTo(radius, 0 + 10);
        rotationPath.cubicTo(radius, 0 + 10, radius * 2 - 10, 0 + 10, radius * 2 - 10, radius);
        rotationPath.cubicTo(radius * 2 - 10, radius, radius * 2 - 10, radius * 2 - 10, radius, radius * 2 - 10);
        rotationPath.cubicTo(radius, radius * 2 - 10, 0 + 10, radius * 2 - 10, 0 + 10, radius);
        rotationPath.cubicTo(0 + 10, radius, 0 + 10, 0 + 10, radius, 0 + 10);
        rotationPath.close();
        pathMeasure = new PathMeasure(rotationPath, false);
        //下面绘制不同半径的小圆
        canvas.drawCircle(mCurrentPositionOne[0], mCurrentPositionOne[1], 10, circlePaintOne);
        canvas.drawCircle(mCurrentPositionTwo[0], mCurrentPositionTwo[1], 9, circlePaintOne);
        canvas.drawCircle(mCurrentPositionThree[0], mCurrentPositionThree[1], 7, circlePaintOne);
        canvas.drawCircle(mCurrentPositionFour[0], mCurrentPositionFour[1], 5, circlePaintOne);
        canvas.drawCircle(mCurrentPositionFive[0], mCurrentPositionFive[1], 3, circlePaintOne);
        if (currentStatus == -1) {
            Message message = new Message();
            message.what = 4;
            handler.sendMessage(message);
        }
        if (animateOrNot) {
            if (currentStatus == 0) {
                currentStatus = 1;
                new Thread() {            //用线程来统一五个圆的周期
                    @Override
                    public void run() {
                        super.run();
                        try {
                            Log.d("thread", "thread");
                            Thread.sleep(1600);
                            Message message = new Message();
                            message.what = 4;
                            handler.sendMessage(message);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
            }
        }
    }

    //供外部调用,开始动画
    public void startAnimate() {
        if (!animateOrNot) {
            animateOrNot = true;
            currentStatus = -1;
            invalidate();
        }
    }

    //供外部调用,停止动画
    public void stopAnimate() {
        if (animateOrNot) {
            animateOrNot = false;
        }
    }

    //界面被销毁
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopAnimate();
        clearAllAnimation();
    }

    //清除所有动画效果
    private void clearAllAnimation() {
        if (valueAnimatorOne != null){
            if (valueAnimatorOne.isRunning()){
                valueAnimatorOne.cancel();
            }
            valueAnimatorOne.removeAllUpdateListeners();
            valueAnimatorOne = null;
        }
        if (valueAnimatorTwo != null){
            if (valueAnimatorTwo.isRunning()){
                valueAnimatorTwo.cancel();
            }
            valueAnimatorTwo.removeAllUpdateListeners();
            valueAnimatorTwo = null;
        }
        if (valueAnimatorThree != null){
            if (valueAnimatorThree.isRunning()){
                valueAnimatorThree.cancel();
            }
            valueAnimatorThree.removeAllUpdateListeners();
            valueAnimatorThree = null;
        }
        if (valueAnimatorFour != null){
            if (valueAnimatorFour.isRunning()){
                valueAnimatorFour.cancel();
            }
            valueAnimatorFour.removeAllUpdateListeners();
            valueAnimatorFour = null;
        }
        if (valueAnimatorFive != null){
            if (valueAnimatorFive.isRunning()){
                valueAnimatorFive.cancel();
            }
            valueAnimatorFive.removeAllUpdateListeners();
            valueAnimatorFive = null;
        }
    }


    //开始第一个小球的动画
    private void startAnimatorOne() {
        if (valueAnimatorOne == null) {
            Log.d("valueAnimatorOne", "valueAnimatorOne");
            valueAnimatorOne = ValueAnimator.ofFloat(0, pathMeasure.getLength());
            valueAnimatorOne.setDuration(800);
            // 减速插值器
            valueAnimatorOne.setInterpolator(new DecelerateInterpolator());
            valueAnimatorOne.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 获取当前点坐标封装到mCurrentPosition
                    float value = (Float) animation.getAnimatedValue();
                    pathMeasure.getPosTan(value, mCurrentPositionOne, null);
                    postInvalidate();
                }
            });
            valueAnimatorOne.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    finishAnimateOne = 1;
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    finishAnimateOne = 0;
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
        }
        valueAnimatorOne.start();
    }

    //开始第二个小球的动画
    private void startAnimatorTwo() {
        if (valueAnimatorTwo == null) {
            valueAnimatorTwo = ValueAnimator.ofFloat(0, pathMeasure.getLength());
            valueAnimatorTwo.setDuration(1000);
            // 减速插值器
            valueAnimatorTwo.setInterpolator(new DecelerateInterpolator());
            valueAnimatorTwo.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    // 获取当前点坐标封装到mCurrentPosition
                    pathMeasure.getPosTan(value, mCurrentPositionTwo, null);
                    postInvalidate();
                }
            });
            valueAnimatorTwo.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    finishAnimateTwo = 1;
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    finishAnimateTwo = 0;
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
        }
        valueAnimatorTwo.start();
    }

    //开始第三个小球的动画
    private void startAnimatorThree() {
        if (valueAnimatorThree == null) {
            valueAnimatorThree = ValueAnimator.ofFloat(0, pathMeasure.getLength());
            valueAnimatorThree.setDuration(1200);
            // 减速插值器
            valueAnimatorThree.setInterpolator(new DecelerateInterpolator());
            valueAnimatorThree.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    // 获取当前点坐标封装到mCurrentPosition
                    pathMeasure.getPosTan(value, mCurrentPositionThree, null);
                    postInvalidate();
                }
            });
            valueAnimatorThree.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    finishAnimateThree = 1;
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    finishAnimateThree = 0;
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
        }
        valueAnimatorThree.start();
    }

    //开始第四个小球的动画
    private void startAnimatorFour() {
        if (valueAnimatorFour == null) {
            valueAnimatorFour = ValueAnimator.ofFloat(0, pathMeasure.getLength());
            valueAnimatorFour.setDuration(1400);
            // 减速插值器
            valueAnimatorFour.setInterpolator(new DecelerateInterpolator());
            valueAnimatorFour.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    // 获取当前点坐标封装到mCurrentPosition
                    pathMeasure.getPosTan(value, mCurrentPositionFour, null);
                    postInvalidate();
                }
            });
            valueAnimatorFour.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    finishAnimateFour = 1;
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    finishAnimateFour = 0;
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
        }
        valueAnimatorFour.start();
    }

    //开始第五个小球的动画
    private void startAnimatorFive() {
        if (valueAnimatorFive == null) {
            valueAnimatorFive = ValueAnimator.ofFloat(0, pathMeasure.getLength());
            valueAnimatorFive.setDuration(1600);
            // 减速插值器
            valueAnimatorFive.setInterpolator(new DecelerateInterpolator());
            valueAnimatorFive.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float value = (Float) animation.getAnimatedValue();
                    // 获取当前点坐标封装到mCurrentPosition
                    pathMeasure.getPosTan(value, mCurrentPositionFive, null);
                    postInvalidate();
                }
            });
            valueAnimatorFive.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {
                    finishAnimateFive = 1;
                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    finishAnimateFive = 0;
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
        }
        valueAnimatorFive.start();
    }

}复制代码

           喜欢我的文章的,请点击关注我哦。万学冬的掘金


转载于:https://juejin.im/post/5a5461ce5188257327397867

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值