仿雅虎新闻的加载自定义view

首先,来看一下今天我们需要实现的一个效果:

这里写图片描述

就是这么一个效果(最后从网上照了一张adidas的壁纸,希望不要被打)

首先,我们先来分析一下这个动画的实现步骤:

  1. 首先,第一个进入的是一个6个小圆组成的一个大圆的旋转动画
  2. 在旋转动画结束了,伴随着的是一个小圆的扩张和聚合动画
  3. 最后聚合到一个点之后,开启水波纹动画,过渡到呈现加载的内容

首先,我们先来实现第一个效果(小圆的旋转效果):
今天给出的实现方法是:首先通过角度和半径计算得到小圆的中心点,绘制6个小圆,然后开启旋转动画,改变旋转的角度,然后重新绘制6个小圆,就可以了!

下面直接上这一部分的代码:

/**
 * Created by DELL on 2017/9/17.
 * Description : 加载动画
 */

public class YahooLoadingView extends View {

    //中心点X 中心点Y 对角线的一半
    private float centerX,centerY,diagonal;

    //小圆的颜色
    private int[] mColorArray ;

    //每一个圆之间的角度
    private float mAngle;

    //旋转的角度
    private float mRotateAngle = 0;

    //画小圆的画笔
    private Paint mCirclePaint;

    //大圆的半径为整体宽度的1/4
    private float mRotateRadius;
    //小圆的半径
    private float mCircleRadius = 18;
    private LoadingState mLoadingState;
    private ValueAnimator mValueAnimator;
    //加载过程中的背景色
    private int mBackgroundColor;

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

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

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

        mColorArray = context.getResources().getIntArray(R.array.splash_circle_colors);
        mAngle = (float) ((Math.PI*2)/mColorArray.length);
        mBackgroundColor = context.getResources().getColor(R.color.splash_bg);
        //初始化画笔
        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setDither(true);
        mCirclePaint.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //初始化宽 高 对角线
        centerX = w/2;
        centerY = h/2;
        diagonal = (float) (Math.sqrt(w*w+h*h)/2f);
        mRotateRadius = w/6;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(mLoadingState == null){
            mLoadingState = new RotateLoadingState();
        }
        mLoadingState.drawState(canvas);
    }


    //画6个小圆  开启旋转动画
    private void drawCircle(Canvas canvas){
        for(int i = 0;i<mColorArray.length;i++){
            float rotateAngle = mAngle*i + mRotateAngle;
            float x = (float) (centerX+Math.cos(rotateAngle)*mRotateRadius);
            float y = (float) (centerY+Math.sin(rotateAngle)*mRotateRadius);
            mCirclePaint.setColor(mColorArray[i]);
            canvas.drawCircle(x,y,mCircleRadius,mCirclePaint);
        }
    }

    private void drawBackground(Canvas canvas){
        canvas.drawColor(mBackgroundColor);
    }

    //策略模式 分为三种状态
    public abstract class LoadingState{
        public abstract void drawState(Canvas canvas);
    }

    //旋转动画
    public class RotateLoadingState extends LoadingState{

        public RotateLoadingState(){
            mValueAnimator = ValueAnimator.ofFloat(0f,(float) Math.PI*2);
            mValueAnimator.setDuration(5000);
            mValueAnimator.setInterpolator(new LinearInterpolator());
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mRotateAngle = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mValueAnimator.start();
        }

        @Override
        public void drawState(Canvas canvas) {
            //画背景
            drawBackground(canvas);
            //画小圆
            drawCircle(canvas);
        }

        //停止动画
        public void cancelAnimator(){
            if(mValueAnimator != null){
                mValueAnimator.cancel();
            }
        }
    }
}

来一张实现之后的效果图:

这里写图片描述

第二步:需要实现的是一个聚合动画
这里的聚合动画分为两步,第一步是扩展动画,扩展完成时候实现收缩动画。这里扩展动画可以合并到收缩动画中一起执行,如何实现呢?自然是通过插值器了!这个插值器叫OvershootInterpolator,有一个反弹功能!

public class PolymerizationLoadingState extends LoadingState{

        public PolymerizationLoadingState(){
            mValueAnimator = ValueAnimator.ofFloat(mRotateRadius,0);
            mValueAnimator.setDuration(1500);
            mValueAnimator.setInterpolator(new OvershootInterpolator(20f));
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mRotateRadius = (float) animation.getAnimatedValue();
                    invalidate();
                }

            });
            mValueAnimator.start();
        }

        @Override
        public void drawState(Canvas canvas) {

            //画背景
            drawBackground(canvas);
            //画小圆
            drawCircle(canvas);
        }
    }

    public void setDisappear(){
        if(mLoadingState != null && mLoadingState instanceof RotateLoadingState){
            ((RotateLoadingState) mLoadingState).cancelAnimator();
            post(new Runnable() {
                @Override
                public void run() {
                    mLoadingState = new PolymerizationLoadingState();
                }
            });
        }
    }

在代码中添加了聚合动画的LoadingState(这里使用了策略设计模式,这样的画,根据其当前的状态,便会执行对应的动画)
然后,我们需要在加载动画的地方触发setDisappear()这个方法,切换当前的LoadingState:

private Handler mHandler = new Handler();
    private YahooLoadingView mLoadingview;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView(){
        mLoadingview = (YahooLoadingView) findViewById(R.id.yahoo_loadingview);
        startLoading();
    }

    private void startLoading(){
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mLoadingview.setDisappear();
            }
        },5000);
    }

这里就在MainActivity中做了一个延时操作进行模拟!

好了,我们来看看现在的效果图:

这里写图片描述

下面开始做第三步,实现水波纹的扩散效果:这里使用的实现方式是:使用画笔画一个中空的圆,慢慢向周围扩散:

public class ExpandLoadingState extends LoadingState{

        public ExpandLoadingState(){
            mValueAnimator = ValueAnimator.ofFloat(0,diagonal);
            mValueAnimator.setDuration(400);
            mValueAnimator.setInterpolator(new LinearInterpolator());
            mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mHoldRadius = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mValueAnimator.start();
        }

        @Override
        public void drawState(Canvas canvas) {

            drawBackground(canvas);
        }
    }

然后修改drawableBackground的方法:

private void drawBackground(Canvas canvas){
        if(mHoldRadius>0){
            float stokenWidth = diagonal - mHoldRadius;
            mBackgroundPaint.setStrokeWidth(stokenWidth);
            float radius = mHoldRadius+stokenWidth/2;
            canvas.drawCircle(centerX,centerY,radius,mBackgroundPaint);
        }else{
            canvas.drawColor(mBackgroundColor);
        }
    }

好了 下面 我们来看看整体的效果:
这里写图片描述

到这里,整个效果就完成了!

源码链接:http://download.csdn.net/download/juxinhau/9982712

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值