视差动画 - 雅虎新闻摘要加载

1. 说明


在属性动画这个部分,我们写了仿58同城数据加载、常见多条目筛选、花束直播点赞效果、酷狗音乐引导页后,那么这节课我们就再来写一个效果 —— 视差动画 - 雅虎新闻摘要加载。效果如下:


视差动画 - 雅虎新闻摘要加载_第1张图片
图片.png

效果就是app刚一开始运行,然后6个圆开始旋转,当旋转到一定时间后,就开始缩放,最后又从中间展开,就是这样的一个动画。

2. 思路分析


2.1: app刚一开始运行,然后就出现6个圆,并且开始旋转;
6个圆用画笔去画,开始旋转使用属性动画。
2.2: 旋转一会,然后6个圆先往外边动一动(差值器),然后开始缩放,最后又从中间展开;
这个属于聚合动画,就是先会往外边动一动,然后缩放,最后从中间展开。
什么时候开始聚合,完全取决于访问后台数据的速度,也就是说只要访问到了后台的数据,这个时候就开始缩放。
2.3: 从中间展开,是扩散动画;
只需要画一个圆就行

3. 代码如下


视差动画 - 雅虎新闻摘要加载 SplashView.java

/**
 * Email: [email protected]
 * Created by JackChen 2018/3/12 11:23
 * Version 1.0
 * Params:
 * Description:  视差动画 - 雅虎新闻摘要加载
*/

public class SplashView extends View {

    // 闪屏的状态
    private SplashState mSplashState ;
    // 是否初始化参数
    private boolean mIsIninParams = false ;
    // 大圆里边包含很多小圆 大圆半径 = 整体宽度的 1/4
    private float mRotationRadius ;
    // 每个小圆的半径 小圆半径 = 大圆半径的 1/10
    private float mCircleRadius ;
    // 小圆的颜色列表
    private int[] mCircleColors ;
    // 旋转动画执行的时间
    private final long ROTATION_ANIMATION_TIME = 2000 ;
    // 第二个部分动画执行的总时间(包括3个动画执行的时间)
    private final long SPLASH_ANIMATION_TIME = 1200 ;
    // 整体的颜色背景
    private int mSplashColor = Color.WHITE ;

    /**
     * 一些变化的参数
     */
    // 空心圆初始半径
    private float mHoleRadius = 0F;
    // 当前大圆旋转的角度(弧度)
    private float mCurrentRotationAngle = 0F ;
    // 当前大圆的半径
    private float mCurrentRotationRadius = mRotationRadius ;
    // 绘制圆的画笔
    private Paint mPaint = new Paint() ;
    // 绘制背景的画笔
    private Paint mPaintBackground = new Paint() ;

    // 屏幕中心的位置
    private int mCenterX , mCenterY;
    // 屏幕对角线一半
    private float mDiagoalDist ;

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

    public SplashView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public SplashView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        if (!mIsIninParams){
            initParams() ;
            mIsIninParams = true ;
        }

        if (mSplashState == null){
            mSplashState = new RotationState() ;
        }

        mSplashState.draw(canvas);
    }


    /**
     * 绘制小圆的旋转动画
     */
    private class RotationState extends SplashState{

        private ValueAnimator mAnimator ;

        public RotationState(){
            mAnimator = ValueAnimator.ofFloat(0,(float) Math.PI*2) ;
            mAnimator.setDuration(ROTATION_ANIMATION_TIME) ;
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 不断获取值 当前旋转的角度
                    mCurrentRotationAngle = (float) animation.getAnimatedValue();
                    // 提醒View重写绘制
                    invalidate();
                }
            });
            mAnimator.setRepeatCount(-1);
            mAnimator.setInterpolator(new LinearInterpolator());
            mAnimator.start();
        }

        @Override
        public void draw(Canvas canvas) {
            canvas.drawColor(mSplashColor);
            // 绘制六个小圆 坐标
            // 之前的角度
            float preAngle = (float) (2*Math.PI/mCircleColors.length);
            for (int i = 0; i < mCircleColors.length; i++) {
                mPaint.setColor(mCircleColors[i]);
                // 初始角度 + 当前旋转的角度
                double angle = i * preAngle + mCurrentRotationAngle;
                float cx = (float) (mCenterX + mRotationRadius * Math.cos(angle));
                float cy = (float) (mCenterY + mRotationRadius * Math.sin(angle));
                canvas.drawCircle(cx,cy,mCircleRadius ,mPaint);
            }
        }

        public void cancelAnimator() {
            mAnimator.cancel();
            mAnimator = null ;
        }
    }


    /**
     * 绘制小圆的聚合动画
     */
    private class MergeState extends SplashState{

        private ValueAnimator mAnimator ;
        public MergeState(){
            // 属性动画
            mAnimator = ValueAnimator.ofFloat(mRotationRadius,1) ;
            mAnimator.setDuration(SPLASH_ANIMATION_TIME/2) ;
            mAnimator.setInterpolator(new AnticipateInterpolator(6f));
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 不断获取值 当前大圆旋转的角度
                    mCurrentRotationAngle = (float) animation.getAnimatedValue();
                    // 提醒View重新绘制
                    invalidate();
                }
            });

            mAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mSplashState = new ExpandSplate() ;
                }
            });

            mAnimator.start();

        }
        @Override
        public void draw(Canvas canvas) {
            canvas.drawColor(mSplashColor);
            // 绘制六个圆 坐标
            float preAngle = (float) (2 * Math.PI / mCircleColors.length);
            for (int i = 0; i < mCircleColors.length; i++) {
                mPaint.setColor(mCircleColors[i]);
                // 初始角度 + 当前旋转的角度
                double angle = i * preAngle + mCurrentRotationAngle;
                float cx = (float) (mCenterX + mCurrentRotationRadius * Math.cos(angle));
                float cy = (float) (mCenterY + mCurrentRotationRadius * Math.sin(angle));
                canvas.drawCircle(cx,cy,mCircleRadius ,mPaint);
            }

        }
    }


    /**
     * 初始化一些参数
     */
    private void initParams() {
        int width = getMeasuredWidth() ;
        int height = getMeasuredHeight() ;

        // 大圆半径
        mRotationRadius = width / 4 ;
        // 小圆半径
        mCircleRadius = mRotationRadius / 8 ;
        // 屏幕中心的位置
        mCenterX = width/2 ;
        mCenterY = height/2 ;
        // 小圆颜色列表
        mCircleColors = getContext().getResources().getIntArray(R.array.splash_circle_colors) ;

        // 绘制圆的抗锯齿
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        // 绘制大圆
        mPaintBackground.setAntiAlias(true);
        mPaintBackground.setDither(true);
        mPaintBackground.setStyle(Paint.Style.STROKE);
        mPaintBackground.setColor(mSplashColor);
        // 屏幕对角线的一半
        mDiagoalDist = (float) Math.sqrt(mCenterX * mCenterX + mCenterY * mCenterY);
    }


    /**
     * 消失
     */
    public void disappear(){
        if (mSplashState instanceof RotationState){
            RotationState rs = (RotationState) mSplashState;
            rs.cancelAnimator() ;
            // 进入下一个动画
            mSplashState = new MergeState();
        }
    }


    /**
     * 绘制小圆的扩散动画
     */
    private class ExpandSplate extends SplashState {

        private ValueAnimator mAnimator ;
        public ExpandSplate(){
            mAnimator = ValueAnimator.ofFloat(0 , mDiagoalDist) ;
            mAnimator.setDuration(SPLASH_ANIMATION_TIME/2) ;
            mAnimator.setInterpolator(new AccelerateInterpolator());
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 不断获取值 当前大圆旋转的角度
                    mHoleRadius = (float) animation.getAnimatedValue();
                    // 提醒View重新绘制
                    invalidate();
                }
            });

            mAnimator.start();
        }
        @Override
        public void draw(Canvas canvas) {
            if (mHoleRadius > 0){
                float strokeWidth = mDiagoalDist - mHoleRadius;
                mPaintBackground.setStrokeWidth(strokeWidth);
                float radius = mHoleRadius + strokeWidth / 2;
                canvas.drawCircle(mCenterX,mCenterY,radius,mPaintBackground);
            }else{
                canvas.drawColor(mSplashColor);
            }
        }
    }

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

activity_main.xml文件中直接引用即可





    


    


MainActivity.java中让其延迟播放2秒后消失:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final SplashView splashView = (SplashView) findViewById(R.id.thirdScreenView);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                splashView.disappear();
            }
        } , 2000) ;

    }
}

具体代码已上传至github:
https://github.com/shuai999/View_day28.git

你可能感兴趣的:(视差动画 - 雅虎新闻摘要加载)