自定义UI——网络加载的自定义View

本文介绍了一种自定义的加载动画View,通过实现旋转、聚合和扩散动画,提升用户在数据加载过程中的体验。动画分为三部分:小圆旋转、加载完成后的聚合以及扩散消失,最终展示布局内容。

我们在开发过程中网络加载通过异步加载加上弹窗、自定义各种progressbar、自定义动画view来解决用户等待时的体验问题,下面介绍一种本人学习时写的自定义的view:

该过程动画主要分三部分,加载时的小圆旋转,加载完成先聚合然后扩散消失显示布局内容,下面看代码:

package com.demo.loadview;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.OvershootInterpolator;

/**
 * Created by yueban on 2018/7/24.
 */

public class LoadView extends View {
    //屏幕对角线尺寸
    private float mDiagonalSize;
    private Paint paintCircle;
    private Paint paintBackground;
    //背景颜色  有时候我们加载全屏遮盖体验不好  可将背景色设置透明
    private int bgColor = Color.WHITE;
    private ValueAnimator mAnimator;
    //旋转一周时间
    private long mRotateTime = 1500;
    private AnimatorState mState;
    //当前旋转的角度
    private float mCurrentDegree;
    private int[] mCircleColors;
    //大圆半径
    private int mBigCircleRadus = 80;
    //小圆半径
    private int mSmallCircleRadus = 18;
    private float mCenterX;
    private float mCenterY;
    //扩散半径
    private float mSpreadRadus = 0f;
    //扩散时间
    private long mSpreadTime = 1000;
    //收缩时当前大圆半径
    private float mCurrentBigRadus = mBigCircleRadus;

    public LoadView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

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

    public LoadView(Context context) {
        super(context);
        init(context);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCenterX = w / 2f;
        mCenterY = h / 2f;
        mDiagonalSize = (float) Math.sqrt(w * w + h * h) / 2f;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mState == null) {
            mState = new RotateState();
        }
        mState.drawAnimator(canvas);
    }

    private void init(Context context) {
        mCircleColors = context.getResources().getIntArray(R.array.splash_circle_colors);
        paintCircle = new Paint();
        paintBackground = new Paint();
        paintCircle.setAntiAlias(true);
        paintBackground.setAntiAlias(true);
        paintBackground.setColor(bgColor);
        //Paint.Style.STROKE 只绘制图形轮廓(描边)
        //Paint.Style.FILL 只绘制图形内容
        //Paint.Style.FILL_AND_STROKE 既绘制轮廓也绘制内容
        paintBackground.setStyle(Paint.Style.STROKE);
    }

    /**
     * 策略模式基类 提供分别做动画的画法操作函数和取消函数
     */
    private abstract class AnimatorState {
        public abstract void drawAnimator(Canvas canvas);

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

    /**
     * 加载完成后结束旋转进行聚合动画
     */
    public void endRotate() {
        if (mState != null && mState instanceof RotateState) {
            RotateState rotateState = (RotateState) mState;
            rotateState.cancel();
            post(new Runnable() {
                @Override
                public void run() {
                    mState = new ContractState();
                }
            });
        }
    }

    //旋转动画
    private class RotateState extends AnimatorState {
        public RotateState() {
            mAnimator = ValueAnimator.ofFloat(0, 2 * (float) Math.PI);
            mAnimator.setInterpolator(new LinearInterpolator());//匀速
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCurrentDegree = (float) animation.getAnimatedValue();
                    postInvalidate();
                }
            });
            mAnimator.setDuration(mRotateTime);
            mAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mAnimator.start();
        }

        @Override
        public void drawAnimator(Canvas canvas) {
            drawBackground(canvas);
            drawCircle(canvas);
        }
    }

    private void drawCircle(Canvas canvas) {
        //每个小圆相隔的角度
        float rotateDegree = (float) (Math.PI * 2 / mCircleColors.length);
        for (int i = 0; i < mCircleColors.length; i++) {
            float mDegree = rotateDegree * i + mCurrentDegree;
            float centerX = (float) (mCurrentBigRadus * Math.cos(mDegree));
            float centerY = (float) (mCurrentBigRadus * Math.sin(mDegree));
            paintCircle.setColor(mCircleColors[i]);
            canvas.drawCircle(mCenterX + centerX, mCenterY + centerY, mSmallCircleRadus, paintCircle);
        }
    }

    private void drawBackground(Canvas canvas) {
        if (mSpreadRadus > 0f) {
            //轮廓宽度
            float stroke = mDiagonalSize - mSpreadRadus;
            //高亮半径
            float mCurrentHoolRadus = mSpreadRadus + stroke / 2;
            paintBackground.setStrokeWidth(stroke);
            canvas.drawCircle(mCenterX, mCenterY, mCurrentHoolRadus, paintBackground);
        } else {
            canvas.drawColor(bgColor);
        }
    }

    //收缩状态
    private class ContractState extends AnimatorState {
        public ContractState() {
            mAnimator = ValueAnimator.ofFloat(0, mBigCircleRadus);
            mAnimator.setInterpolator(new OvershootInterpolator());
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCurrentBigRadus = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    //动画结束进行扩散
                    mState = new SpreadState();
                }
            });
            mAnimator.setDuration(mRotateTime);
            mAnimator.reverse();
        }

        @Override
        public void drawAnimator(Canvas canvas) {
            drawBackground(canvas);
            drawCircle(canvas);
        }
    }

    //扩散状态
    private class SpreadState extends AnimatorState {

        public SpreadState() {
            mAnimator = ValueAnimator.ofFloat(mSmallCircleRadus, mDiagonalSize);
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mSpreadRadus = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            mAnimator.setDuration(mSpreadTime);
            mAnimator.start();
        }

        @Override
        public void drawAnimator(Canvas canvas) {
            drawBackground(canvas);
        }
    }
}

使用时只需在内容跟布局控件添加该view对象即可:

public class MainActivity extends AppCompatActivity {

    private ImageView img;
    private LoadView loadView;
    private FrameLayout parent;
    private FrameLayout mMainView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        parent = findViewById(R.id.parent);
        loadView = new LoadView(this);
        parent.addView(loadView);
        startLoadData();
    }

    Handler handler = new Handler();

    private void startLoadData() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                //数据加载完毕,进入主界面--->开启后面的两个动画
                loadView.endRotate();
            }
        }, 5000);//延迟时间
    }

动画效果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值