属性动画案例:雅虎加载视差动画

实现效果:
这里写图片描述

这边其实是两个View,加载动画覆盖在需要进入的页面的上面
动画分为三种状态
1:一开始的小圆球旋转动画
2:小圆逃逸后再聚合动画
3:水波纹扩散动画

一、初始化基本参数

    private void init(Context context) {
        //这边mCircleColors为六个小球的颜色
        mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);
        //画笔初始化
        //消除锯齿
        mPaint.setAntiAlias(true);
        mPaintBackground.setAntiAlias(true);//该Paint为加载动画白色背景墙
        //设置样式---边框样式--描边
        mPaintBackground.setStyle(Paint.Style.STROKE);
        mPaintBackground.setColor(mSplashBgColor);
    }

二、使用策略模式分别实现三种状态

    private SplashState mState=null;

    private abstract class SplashState{
        public abstract void drawSate(Canvas canvas);

    }

先定义好是要实现的策略类

1)旋转动画

    /**
     * 1.旋转动画
     * 控制各个小圆的坐标----控制小圆的角度变化------属性动画ValueAnimator
     */
    private class RotateState extends SplashState{
        public RotateState() {
            //1.动画的初始工作, 0~2π 圆周角的弧度数
            mAnimator=ValueAnimator.ofFloat(0f,(float)(Math.PI*2));
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //计算某个时刻当前的角度是多少
                    mCurrentRotationAngle= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            // 2.开启动画
            mAnimator.setInterpolator(new LinearInterpolator());
            mAnimator.setDuration(mRotationDuration);
            mAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mAnimator.start();
        }

        public void cancel(){
            mAnimator.cancel();
        }

        @Override
        public void drawSate(Canvas canvas) {
            //1.背景---擦黑板,涂成白色
            drawBackground(canvas);
            //2.绘制小圆
            drawCircles(canvas);
        }
    }

构造方法中初始化好旋转动画所需的属性动画

绘制小圆的方法

private void drawCircles(Canvas canvas) {
        //每个小圆之间的间隔角度2π/小圆的个数
        float rotationAngel= (float) (2*Math.PI/mCircleColors.length);
        for (int i=0;i<mCircleColors.length;i++){
            /*
             *  x=r*cos(a)+centerX
             *  y=r*sin(a)+centerY
             */
            double angle=i*rotationAngel+mCurrentRotationAngle;
            //每个小圆i*间隔角度+旋转角度=当前小圆的真实角度
            float cx= (float) (mCurrentRotationRadius*Math.cos(angle)+mCenterX);
            float cy= (float) (mCurrentRotationRadius*Math.sin(angle)+mCenterY);
            mPaint.setColor(mCircleColors[i]);
            canvas.drawCircle(cx,cy,mCircleRadius,mPaint);
        }

    }

1.首先先求出一个由六个小圆组成大圆之间它们之间的间隔角度为2π/小球个数
2.再根据属性动画mCurrentRotationAngle计算的旋转角度以及间隔角度算出当前小球的真实角度
3.mCenterX和mCenterY取的是屏幕宽高的一半,mCurrentRotationRadius为定义的大圆的半径
这里写图片描述
4.这样就算出了小圆的圆心坐标

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(mState==null){
            //开启第一个动画,旋转动画
            mState=new RotateState();
        }

        //调用绘制方法
        mState.drawSate(canvas);
    }

在onDraw开启第一个动画,并调用绘制方法开始无限循环的旋转小球,在主界面顶一个延迟来结束第一个动画进入第二个动画

    private void startLoadData() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //数据加载完毕,进入主界面---》开启后面的两个动画
                mSplashView.splashDisapper();
            }
        },3000);
    }

自定义View中:

    public void splashDisapper(){
        //开启后面两个动画

        //换模板if--换状态
        if(mState!=null&&mState instanceof RotateState){
            //结束旋转动画
            RotateState rotateState= (RotateState) mState;
            rotateState.cancel();

            post(new Runnable() {
                @Override
                public void run() {
                    mState=new MergingState();
                }
            });
        }
    }

2)聚合动画

    /**
     * 2.聚合动画
     * 要素:大圆的半径不断地变大---》变小----》小圆的坐标
     */
    private class MergingState extends SplashState{
        public MergingState() {
            //r~0的某个值
            mAnimator=ValueAnimator.ofFloat(mRotationRadius,0);
            mAnimator.setDuration(mRotationDuration);
            mAnimator.setInterpolator(new OvershootInterpolator(10f));
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //某个时刻当前的大圆半径是多少?
                    mCurrentRotationRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    mState=new ExpandState();
                }
            });
            mAnimator.start();
        }

        @Override
        public void drawSate(Canvas canvas) {
            //1.背景---擦黑板,涂成白色
            drawBackground(canvas);
            //2.绘制小圆
            drawCircles(canvas);
        }
    }

1.mRotationRadius大圆初始半径到0的值,加入插值器OvershootInterpolator(向前甩一定值后再回到原来位置)
2.当持续时间完成调用下一个动画,这边drawState中同样是需要画白色背景墙和小圆的,只是这个时候mCurrentRotationRadius大圆半径进行了变化

3)水波纹扩散动画

    /**
     * 3.水波纹扩散动画
     * 画一个空心圆,让它的画笔的粗细变成很大---不断地减少画笔的粗细
     * 空心圆变化的范围:0~对角线/2
     */
    private class ExpandState extends SplashState{
        public ExpandState() {
            //1200ms,计算某个时刻当前的大圆半径是多少?r~对角线一半的某个值
            mAnimator=ValueAnimator.ofFloat(mCircleRadius,mDiagonalDist);
            mAnimator.setDuration(mRotationDuration);
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mHoleRadius= (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mAnimator.start();
        }

        @Override
        public void drawSate(Canvas canvas) {
            //1.背景---擦黑板,涂成白色
            drawBackground(canvas);
        }
    }

1.第三个动画实现方式根据画一个空心圆,控制其stroWidth大小,不断减小实现扩散
2.mHoleRadius为空心圆的初始半径,这个时候处在第三状态的一开始所有的小球是已经聚集在一起的,所以初始半径这边根据属性动画mCircleRadius(小圆半径)到mDiagonalDist
3.mDiagonalDist:为屏幕对角线的一半,至于为什么是这个需要看一下drawBackground方法

    private void drawBackground(Canvas canvas) {
        if(mHoleRadius>0f){//第三个动画开始执行
            //得到画笔的宽度---对角线的一半-空心圆的半径
            float stroWidth=mDiagonalDist-mHoleRadius;
            mPaintBackground.setStrokeWidth(stroWidth);
            //画圆的半径=空心圆的半径+画笔的宽度/2
            float radius=mHoleRadius+stroWidth/2;
            canvas.drawCircle(mCenterX,mCenterY,radius,mPaintBackground);
        }else{//其他两个动画
            canvas.drawColor(mSplashBgColor);
        }
    }

这里写图片描述

这样空心圆半径不断变大,stroWdith不断减少,圆半径随之改变,就实现了扩散的方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值