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

来源:互联网 发布:系统数据流程图 编辑:程序博客网 时间:2024/05/21 06:31

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

这边其实是两个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不断减少,圆半径随之改变,就实现了扩散的方法

原创粉丝点击