自定义Loading大全之SwapLoading

  那一月 我摇动所有的经筒 不为超度 只为触摸你的指尖
  那一年 磕长头匍匐在山路 不为觐见 只为贴着你的温暖
  那一世 转山转水转佛塔 不为修来生 只为途中与你相见
  那一瞬,我飞升成仙,不为长生,只为佑你平安喜乐

概述

我们经常会看到一些很简单又非常耐看的Loading加载动画,它们又是怎么用代码实现出来的呢?通过代码实现简单的Loading动画可以帮助我们学到很多东西,尤其是一些算法的运用,任何事情都是从繁到简,日积夜累,正所谓你看得多了你知道的也就多了。。。

还是老规矩,上效果图:

swap

源码请点击自定义LoadingView大全

今天的主角登场:

swap

SwapLoading

开撸之前,我们来分析一下动画:

  • 有5个小圆,编号 a,b,c,d,e a顺时针向后面运动到b的位置,同时b顺时针运动到a的位置,依次类推运动到e,e顺时针运动到a的同时a运动到e形成一个闭环。

需要注意的是:a运动到b的轨迹是半圆。最开始我就踩了一个坑,把轨迹想成了二阶贝塞尔曲线,一直纠结曲线公式,弄得我头都大了。后来灵感一闪,我傻啊,为啥不用圆形轨迹,哈哈一下我就豁然开朗啦。

分析一下,是不是很清晰,然后开撸。

前面的一些构造函数就初始化就不讲了,省略onMeasure,onSizeChanged方法。注意需要在onSizeChanged方法中处理padding属性。

先来看一张简单的草稿图,图画得不好,见谅。

swap

我还是用 a,b,c,d,e来表示小球,a小球向上顺时针运动,轨迹所经过的圆弧为180~360:

 mRotationAngle = DEGREE_180 + mValueAnimator * DEGREE_180;

mValueAnimator表示属性动画的值(0.0f~1.0f)

b小球所经过的圆弧为0~180度:

 mRotationAngle = DEGREE_180 - mValueAnimator * DEGREE_180;

a,b小球所运动的半径为2小球之间的距离。那么我们简单的实现2个小球的运动轨迹。

swap

需要注意从b运动到c,b所经过的弧度为180~0:

 mRotationAngle = DEGREE_180 - mValueAnimator * DEGREE_180;

c经过的弧度为0~-180度:

  mNextRotationAngle = -mValueAnimator * DEGREE_180;

依次类推可以实现a,b,c,d小球的运动动画,小球a到e就非常简单了,注意运动半径是2*小球间距。

我这里贴出onDraw和startAnimator方法代码,以供参考:

onDraw方法:


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setColor(Color.WHITE);
        //绘制小球
        for (int i = 0; i < BALL_NUM; i++) {
            if (i == mCurrentBallIndex && i != BALL_NUM - 1) {
                if (isClockwiseRotation(i)) {
                    mRotationAngle = DEGREE_180 + mValueAnimator * DEGREE_180;
                    mNextRotationAngle = mValueAnimator * DEGREE_180;
                } else {
                    mRotationAngle = DEGREE_180 - mValueAnimator * DEGREE_180;
                    mNextRotationAngle = -mValueAnimator * DEGREE_180;
                }
            }

            if (i == mCurrentBallIndex) {
                mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

                mRotationX = (float) (ballSpacing * BALL_SPACING_RADIUS * Math.cos(Math.toRadians(mRotationAngle)));
                mRotationY = (float) (ballSpacing * BALL_SPACING_RADIUS * Math.sin(Math.toRadians(mRotationAngle)));

                if (i == BALL_NUM - 1) {
                    getFirstAndLastAngle();
                    //最后一个小球
                    canvas.drawCircle((float) (centerX + 2 * ballSpacing * Math.cos(Math.toRadians(mNextRotationAngle))),
                            (float) (centerY + 2 * ballSpacing * Math.sin(Math.toRadians(mNextRotationAngle))),
                            ballRadius, mPaint);
                } else {
                    canvas.drawCircle(centerX - mRadius + ballSpacing * (BALL_SPACING_RADIUS + i) + mRotationX,
                            centerY + mRotationY, ballRadius, mPaint);
                }
            }

            if ((i + 1) % BALL_NUM == getNextBallIndex()) {
                mPaint.setStyle(Paint.Style.STROKE);

                mRotationX = (float) (ballSpacing * BALL_SPACING_RADIUS * Math.cos(Math.toRadians(mNextRotationAngle)));
                mRotationY = (float) (ballSpacing * BALL_SPACING_RADIUS * Math.sin(Math.toRadians(mNextRotationAngle)));

                if (i == BALL_NUM - 1) {
                    getFirstAndLastAngle();
                    //第一个小球
                    canvas.drawCircle((float) (centerX + 2 * ballSpacing * Math.cos(Math.toRadians(mRotationAngle))),
                            (float) (centerY + 2 * ballSpacing * Math.sin(Math.toRadians(mRotationAngle))),
                            ballRadius, mPaint);

                } else {
                    canvas.drawCircle(centerX - mRadius + ballSpacing * (BALL_SPACING_RADIUS + i) + mRotationX,
                            centerY + mRotationY, ballRadius, mPaint);
                }
            }


            if (i != mCurrentBallIndex && i != getNextBallIndex()) {
                mPaint.setStyle(Paint.Style.STROKE);
                canvas.drawCircle(centerX - mRadius + ballSpacing * i, centerY, ballRadius, mPaint);
            }

        }


        // centerX - mRadius + ballSpacing*0.5f      centerY


    }

startAnimator方法:


    //开始动画
    public void startAnimator() {
        post(new Runnable() {
            @Override
            public void run() {
                ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);
                animator.setDuration(5000);
                animator.setInterpolator(new LinearInterpolator());
                animator.setRepeatMode(ValueAnimator.RESTART);
                animator.setRepeatCount(ValueAnimator.INFINITE);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        mValueAnimator = (float) animation.getAnimatedValue();

                        postInvalidate();
                    }
                });
                animator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                        super.onAnimationStart(animation);
                        mCurrentBallIndex = 0;
                    }

                    @Override
                    public void onAnimationRepeat(Animator animation) {
                        super.onAnimationRepeat(animation);
                        mCurrentBallIndex = getNextBallIndex();

                    }
                });
                animator.start();
            }
        });
    }

这样就实现了SwapLoading加载动画。

敬请关注github源码地址

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值