Android仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)

仿QQ邮箱下拉刷新动画(三个小球围绕中心转动)

该动画的实现主要借鉴了海龙的博客- 两个小球不停的绕中心旋转的进度条 ,在此感谢下。

1 首先上图(折腾了好久才把gif给搞了上去

这里写图片描述

2 分析

2.1 当我们看到一个动画,首先需要对动画的效果进行分析,而不是盲目的进行开发

2.2 动画的分解(先需要关注一个小球的效果,避免其他干扰)
2.2.1 平移动画:把中心点的横坐标当作坐标的原点
第一个小球的x轴变化为:-1f>0f>1f>0f>-1f;
第二个小球的x轴变化为:0f>1f>0f>-1f>0f;
第三个小球的x轴变化为:1f>0f>-1f>0f>1f;
2.2.2 缩放动画:(三种大小:minRadius,centerRadius,maxRadius)
第一个小球的缩放变化为:center>max>center>min>center;
第二个小球的缩放变化为:max>center>min>center>max;
第三个小球的缩放变化为:center>min>center>max>center;
2.2.3 重要的一点:简单理解为半径大的覆盖在半径小的上方

3 分析完毕,直接上代码

public class ThreeBallRotationProgressBar extends View {

    private final static int DEFAULT_MAX_RADIUS = 16;
    private final static int DEFAULT_MIN_RADIUS = 5;
    private final static int DEFAULT_DISTANCE = 35;

    private final static int DEFAULT_ONE_BALL_COLOR = Color
            .parseColor("#40df73");
    private final static int DEFAULT_TWO_BALL_COLOR = Color
            .parseColor("#ffdf3e");
    private final static int DEFAULT_THREE_BALL_COLOR = Color
            .parseColor("#ff733e");

    private final static int DEFAULT_ANIMATOR_DURATION = 1400;

    private Paint mOnePaint;
    private Paint mTwoPaint;
    private Paint mThreePaint;

    private float maxRadius = DEFAULT_MAX_RADIUS;
    private float minRadius = DEFAULT_MIN_RADIUS;

    private int distance = DEFAULT_DISTANCE;

    private long duration = DEFAULT_ANIMATOR_DURATION;

    private Ball mOneBall;
    private Ball mTwoBall;
    private Ball mThreeBall;

    private float mCenterX;
    private float mCenterY;

    private AnimatorSet animatorSet;

    public ThreeBallRotationProgressBar(Context context) {
        this(context, null);
    }

    public ThreeBallRotationProgressBar(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ThreeBallRotationProgressBar(Context context, AttributeSet attrs,
            int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {

        mOneBall = new Ball();
        mTwoBall = new Ball();
        mThreeBall = new Ball();

        mOneBall.setColor(DEFAULT_ONE_BALL_COLOR);
        mTwoBall.setColor(DEFAULT_TWO_BALL_COLOR);
        mThreeBall.setColor(DEFAULT_THREE_BALL_COLOR);

        mOnePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mOnePaint.setColor(DEFAULT_ONE_BALL_COLOR);
        mTwoPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTwoPaint.setColor(DEFAULT_TWO_BALL_COLOR);
        mThreePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mThreePaint.setColor(DEFAULT_THREE_BALL_COLOR);

        configAnimator();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCenterX = w / 2;
        mCenterY = h / 2;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mCenterX = getWidth() / 2;
        mCenterY = getHeight() / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mOneBall.getRadius() >= mTwoBall.getRadius()) {
            if (mThreeBall.getRadius() >= mOneBall.getRadius()) {
                canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,
                        mTwoBall.getRadius(), mTwoPaint);
                canvas.drawCircle(mOneBall.getCenterX(), mCenterY,
                        mOneBall.getRadius(), mOnePaint);
                canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,
                        mThreeBall.getRadius(), mThreePaint);
            } else {
                if (mTwoBall.getRadius() <= mThreeBall.getRadius()) {
                    canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,
                            mTwoBall.getRadius(), mTwoPaint);
                    canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,
                            mThreeBall.getRadius(), mThreePaint);
                    canvas.drawCircle(mOneBall.getCenterX(), mCenterY,
                            mOneBall.getRadius(), mOnePaint);
                } else {
                    canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,
                            mThreeBall.getRadius(), mThreePaint);
                    canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,
                            mTwoBall.getRadius(), mTwoPaint);
                    canvas.drawCircle(mOneBall.getCenterX(), mCenterY,
                            mOneBall.getRadius(), mOnePaint);

                }
            }
        } else {
            if (mThreeBall.getRadius() >= mTwoBall.getRadius()) {
                canvas.drawCircle(mOneBall.getCenterX(), mCenterY,
                        mOneBall.getRadius(), mOnePaint);
                canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,
                        mTwoBall.getRadius(), mTwoPaint);
                canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,
                        mThreeBall.getRadius(), mThreePaint);
            } else {
                if (mOneBall.getRadius() <= mThreeBall.getRadius()) {
                    canvas.drawCircle(mOneBall.getCenterX(), mCenterY,
                            mOneBall.getRadius(), mOnePaint);
                    canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,
                            mThreeBall.getRadius(), mThreePaint);
                    canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,
                            mTwoBall.getRadius(), mTwoPaint);
                } else {
                    canvas.drawCircle(mThreeBall.getCenterX(), mCenterY,
                            mThreeBall.getRadius(), mThreePaint);
                    canvas.drawCircle(mOneBall.getCenterX(), mCenterY,
                            mOneBall.getRadius(), mOnePaint);
                    canvas.drawCircle(mTwoBall.getCenterX(), mCenterY,
                            mTwoBall.getRadius(), mTwoPaint);

                }
            }
        }

    }

    private void configAnimator() {
        float centerRadius = (maxRadius + minRadius) * 0.5f;

        ObjectAnimator oneScaleAnimator = ObjectAnimator.ofFloat(mOneBall,
                "radius", centerRadius, maxRadius, centerRadius, minRadius,
                centerRadius);
        oneScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);
        ValueAnimator oneCenterAnimator = ValueAnimator
                .ofFloat(-1, 0, 1, 0, -1);
        oneCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);
        oneCenterAnimator
                .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (Float) animation.getAnimatedValue();
                        float x = mCenterX + (distance) * value;
                        mOneBall.setCenterX(x);
                        invalidate();
                    }
                });
        ValueAnimator oneAlphaAnimator = ValueAnimator.ofFloat(0.8f, 1, 0.8f,
                0, 0.8f);
        oneAlphaAnimator.setRepeatCount(ValueAnimator.INFINITE);
        oneAlphaAnimator
                .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (Float) animation.getAnimatedValue();
                        int alpha = (int) (255 * value);
                        mOnePaint.setAlpha(alpha);
                    }
                });

        ObjectAnimator twoScaleAnimator = ObjectAnimator.ofFloat(mTwoBall,
                "radius", maxRadius, centerRadius, minRadius, centerRadius,
                maxRadius);
        twoScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);

        ValueAnimator twoCenterAnimator = ValueAnimator.ofFloat(0, 1, 0, -1, 0);
        twoCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);
        twoCenterAnimator
                .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (Float) animation.getAnimatedValue();
                        float x = mCenterX + (distance) * value;
                        mTwoBall.setCenterX(x);
                    }
                });
        ValueAnimator twoAlphaAnimator = ValueAnimator.ofFloat(1, 0.8f, 0,
                0.8f, 1);
        twoAlphaAnimator.setRepeatCount(ValueAnimator.INFINITE);
        twoAlphaAnimator
                .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (Float) animation.getAnimatedValue();
                        int alpha = (int) (255 * value);
                        mTwoPaint.setAlpha(alpha);
                    }
                });

        ObjectAnimator threeScaleAnimator = ObjectAnimator.ofFloat(mThreeBall,
                "radius", centerRadius, minRadius, centerRadius, maxRadius,
                centerRadius);
        threeScaleAnimator.setRepeatCount(ValueAnimator.INFINITE);

        ValueAnimator threeCenterAnimator = ValueAnimator.ofFloat(1, 0, -1, 0,
                1);
        threeCenterAnimator.setRepeatCount(ValueAnimator.INFINITE);
        threeCenterAnimator
                .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (Float) animation.getAnimatedValue();
                        float x = mCenterX + (distance) * value;
                        mThreeBall.setCenterX(x);
                    }
                });
        ValueAnimator threeAlphaAnimator = ValueAnimator.ofFloat(0.8f, 0, 0.8f,
                1, 0.8f);
        threeAlphaAnimator.setRepeatCount(ValueAnimator.INFINITE);
        threeAlphaAnimator
                .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        float value = (Float) animation.getAnimatedValue();
                        int alpha = (int) (255 * value);
                        mThreePaint.setAlpha(alpha);
                    }
                });

        animatorSet = new AnimatorSet();
        animatorSet.playTogether(oneScaleAnimator, oneCenterAnimator,
                twoScaleAnimator, twoCenterAnimator, threeScaleAnimator,
                threeCenterAnimator);
        animatorSet.setDuration(DEFAULT_ANIMATOR_DURATION);
        animatorSet.setInterpolator(new LinearInterpolator());
    }

    public class Ball {
        private float radius;
        private float centerX;
        private int color;

        public float getRadius() {
            return radius;
        }

        public void setRadius(float radius) {
            this.radius = radius;
        }

        public float getCenterX() {
            return centerX;
        }

        public void setCenterX(float centerX) {
            this.centerX = centerX;
        }

        public int getColor() {
            return color;
        }

        public void setColor(int color) {
            this.color = color;
        }
    }

    @Override
    public void setVisibility(int v) {
        if (getVisibility() != v) {
            super.setVisibility(v);
            if (v == GONE || v == INVISIBLE) {
                stopAnimator();
            } else {
                startAnimator();
            }
        }
    }

    @Override
    protected void onVisibilityChanged(View changedView, int v) {
        super.onVisibilityChanged(changedView, v);
        if (v == GONE || v == INVISIBLE) {
            stopAnimator();
        } else {
            startAnimator();
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        startAnimator();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopAnimator();
    }

    public void setOneBallColor(int color) {
        mOneBall.setColor(color);
    }

    public void setmTwoBallColor(int color) {
        mTwoBall.setColor(color);
    }

    public void setMaxRadius(float maxRadius) {
        this.maxRadius = maxRadius;
        configAnimator();
    }

    public void setMinRadius(float minRadius) {
        this.minRadius = minRadius;
        configAnimator();
    }

    public void setDistance(int distance) {
        this.distance = distance;
    }

    public void setDuration(long duration) {
        this.duration = duration;
        if (animatorSet != null) {
            animatorSet.setDuration(duration);
        }
    }

    public void startAnimator() {
        if (getVisibility() != VISIBLE)
            return;

        if (animatorSet.isRunning())
            return;

        if (animatorSet != null) {
            animatorSet.start();
        }
    }

    public void stopAnimator() {
        if (animatorSet != null) {
            animatorSet.end();
        }
    }
}

3.1 configAnimator()方法主要就是实现2中分析的动画效果

3.2 为了解决2中提到的重要一点,半径大的小球覆盖在半径小的小球上方,主要在onDraw()采用比较的方式实现

4 总结

4.1 掌阅iReader的下拉刷新也采用了类似的动画效果(三个方形围绕中心转动),大家可以参考本文章,试着实现里边的动画效果。相信自己写过,总能有不少收获的~

4.2 对于android动画,还是需要耐心的分析,当然熟悉的掌握动画实现还是必要的!

5 源码下载

下载地址

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值