美文网首页Android架构师自定义控件
自定义View - 仿华为LoadingView4

自定义View - 仿华为LoadingView4

作者: 西柚9102 | 来源:发表于2020-02-14 22:27 被阅读0次

    作者:MinKin
    链接:https://www.jianshu.com/p/4732d8ae61fe

    介绍

    最近留意到华为手机预装的全家桶软件中,全都采用了统一的一个加载控件(如下图),觉得挺好看,然后就想着自己搞下。

    实现

    1、分析

    2、自定义属性

       <declare-styleable name="HwLoadingView">
            <!--控件大小-->
            <attr name="hlv_size" format="dimension" />
            <!--控件颜色-->
            <attr name="hlv_color" format="color" />
            <!--转一圈花费时长-->
            <attr name="hlv_duration" format="integer"/>
        </declare-styleable>
    
    

    3、源码

    public class HwLoadingView extends View {
    
        private static final int CIRCLE_COUNT = 12;  //小圆总数
        private static final int DEGREE_PER_CIRCLE = 360 / CIRCLE_COUNT;  //小圆圆心之间间隔角度差
        private float[] mWholeCircleRadius = new float[CIRCLE_COUNT];  //记录所有小圆半径
        private int[] mWholeCircleColors = new int[CIRCLE_COUNT]; //记录所有小圆颜色
        private float mMaxCircleRadius;   //小圆最大半径
        private int mSize;     //控件大小
        private int mColor;    //小圆颜色
    
        private Paint mPaint; //画笔
    
        private ValueAnimator mAnimator;
        private int mAnimateValue = 0;
        private long mDuration;   //动画时长
    
        public HwLoadingView(Context context) {
            this(context, null);
        }
    
        public HwLoadingView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public HwLoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initAttrs(context, attrs);
            initPaint();
            initValue();
        }
    
        /**
         * 初始化自定义属性
         */
        private void initAttrs(Context context, AttributeSet attrs) {
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HwLoadingView);
    
            mSize = (int) ta.getDimension(R.styleable.HwLoadingView_hlv_size, dp2px(context, 100));
            setSize(mSize);
    
            mColor = ta.getColor(R.styleable.HwLoadingView_hlv_color, Color.parseColor("#333333"));
            setColor(mColor);
    
            mDuration = ta.getInt(R.styleable.HwLoadingView_hlv_duration, 1500);
    
            ta.recycle();
        }
    
        /**
         * 初始化画笔
         */
        private void initPaint() {
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(mColor);
        }
    
        /**
         * 初始化所有小圆半径、颜色
         */
        private void initValue() {
            float minCircleRadius = mSize / 24;
            for (int i = 0; i < CIRCLE_COUNT; i++) {
                switch (i) {
                    case 7:
                        mWholeCircleRadius[i] = minCircleRadius * 1.25f;
                        mWholeCircleColors[i] = (int) (255 * 0.7f);
                        break;
                    case 8:
                        mWholeCircleRadius[i] = minCircleRadius * 1.5f;
                        mWholeCircleColors[i] = (int) (255 * 0.8f);
                        break;
                    case 9:
                    case 11:
                        mWholeCircleRadius[i] = minCircleRadius * 1.75f;
                        mWholeCircleColors[i] = (int) (255 * 0.9f);
                        break;
                    case 10:
                        mWholeCircleRadius[i] = minCircleRadius * 2f;
                        mWholeCircleColors[i] = 255;
                        break;
                    default:
                        mWholeCircleRadius[i] = minCircleRadius;
                        mWholeCircleColors[i] = (int) (255 * 0.5f);
                        break;
                }
            }
            mMaxCircleRadius = minCircleRadius * 2;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //去除wrap_content、Match_Parent对控件的影响
            setMeasuredDimension(mSize, mSize);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (mSize > 0) {
                //每隔DEGREE_PER_CIRCLE * mAnimateValue角度,绘制所有小圆
                canvas.rotate(DEGREE_PER_CIRCLE * mAnimateValue, mSize / 2, mSize / 2);
                for (int i = 0; i < CIRCLE_COUNT; i++) {
                    //设置小圆颜色
                    mPaint.setAlpha(mWholeCircleColors[i]);
                    //每隔DEGREE_PER_CIRCLE角度,绘制一个小圆
                    canvas.drawCircle(mSize / 2, mMaxCircleRadius, mWholeCircleRadius[i], mPaint);
                    canvas.rotate(DEGREE_PER_CIRCLE, mSize / 2, mSize / 2);
                }
            }
        }
    
        /**
         * 设置控件大小
         */
        public void setSize(int size) {
            mSize = size;
            invalidate();
        }
    
        /**
         * 设置小圆颜色
         */
        public void setColor(@ColorInt int color) {
            mColor = color;
            invalidate();
        }
    
        /**
         * 动画监听
         */
        private ValueAnimator.AnimatorUpdateListener mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
    
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimateValue = (int) animation.getAnimatedValue();
                invalidate();
            }
        };
    
        /**
         * 开始动画
         */
        public void start() {
            if (mAnimator == null) {
                mAnimator = ValueAnimator.ofInt(0, CIRCLE_COUNT - 1);
                mAnimator.addUpdateListener(mUpdateListener);
                mAnimator.setDuration(mDuration);
                mAnimator.setRepeatMode(ValueAnimator.RESTART);
                mAnimator.setRepeatCount(ValueAnimator.INFINITE);
                mAnimator.setInterpolator(new LinearInterpolator());
                mAnimator.start();
            } else if (!mAnimator.isStarted()) {
                mAnimator.start();
            }
        }
    
        /**
         * 停止动画
         */
        public void stop() {
            if (mAnimator != null) {
                mAnimator.removeUpdateListener(mUpdateListener);
                mAnimator.removeAllUpdateListeners();
                mAnimator.cancel();
                mAnimator = null;
            }
        }
    
        /**
         * View依附Window时停止动画
         */
        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            start();
        }
    
        /**
         * View脱离Window时停止动画
         */
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            stop();
        }
    
        /**
         * 根据View可见性变化开始/停止动画
         */
        @Override
        protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
            super.onVisibilityChanged(changedView, visibility);
            if (visibility == VISIBLE) {
                start();
            } else {
                stop();
            }
        }
    
        /**
         * dp转px
         */
        private int dp2px(Context context, float dp) {
            float density = context.getResources().getDisplayMetrics().density;
            return (int) (density * dp + 0.5f);
        }
    }
    
    

    使用

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <com.mk.hwloadingbar.HwLoadingView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:hlv_color="@color/colorAccent"
            app:hlv_size="360dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
    

    最终效果图如下

    最后
    附上我整理收集的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里在石墨文档以图片的形式给大家展示一部分;
    《Android架构视频+BAT面试专题PDF+学习笔记》

    如果感觉本文对你有帮助,请点个赞吧谢谢~

    相关文章

      网友评论

        本文标题:自定义View - 仿华为LoadingView4

        本文链接:https://www.haomeiwen.com/subject/iwrsfhtx.html