Android 简单的自定义view loading circle

 

Android 简单的自定义view loading circle_第1张图片

这个其实是改编了 一个腾讯开源 qmui 里面的效果,但是我们UI 要圆形的就改了下,项目里面是直接复制源码,然后将里面的drawline 改成drawcircle 就好了。

public class LoadingCircleView extends View {

    // 大圆的半径
    private int mWidth;
    private int mHeight;

    private boolean init = false;

    //圆球的个数
    private int mCircleCount;
    private int mCircleWidth;


    private Paint mPaint;

    private int mPaintColor;

    //这里可以多加一个attr 属性值 用来设置大圆的半径(小于getwidth)
    private int mSize;

    //动画
    private ValueAnimator mValueAnimator;
    private int mAnimateValue;//用来计算 旋转角度
    private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mAnimateValue = (int) animation.getAnimatedValue();
            invalidate();
        }
    };

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

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

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


    private void initView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        TypedArray typedArray = context.obtainStyledAttributes(R.styleable.LoadingView);
        mCircleCount = typedArray.getInteger(R.styleable.LoadingView_loading_circle_count, 10);
        mPaintColor = typedArray.getColor(R.styleable.LoadingView_loading_color, Color.BLUE);
        //回收
        typedArray.recycle();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (!init) {
            init = true;
            init();
        }


    }

    private void init() {
        mWidth = getMeasuredWidth() / 4;
        mHeight = getMeasuredHeight();
        mPaint = new Paint();
        mPaint.setColor(mPaintColor);
        mPaint.setStrokeWidth(1);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        //view 大圆圆心 切小圆球的两条切线的圆心角 小于 360/circlecount 极端就是小圆形相切
        // 这里要注意 Math.sin() 参数要传弧度值 看源码注释
        double sin = Math.sin(360 / mCircleCount / 2 * Math.PI / 180);
        //相切的时候 公式 (R-r)*sinA = r; 也可以排松一点 比这个小就好了
        mCircleWidth = (int) (sin * mWidth / 2 / (1 + sin));
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        drawCircles(canvas);
        canvas.restoreToCount(saveCount);
    }

    private void drawCircles(Canvas canvas) {
        //1.动画的时候需要旋转
        canvas.rotate(360 / mCircleCount * mAnimateValue,getWidth()/2,getHeight()/2);
        //2.将坐标平移到 外圆中心
        canvas.translate(getMeasuredWidth() / 2, mHeight / 2);
        //3.画圆球 每次旋转 360/count
        for (int i = 0; i < mCircleCount; i++) {
            mPaint.setAlpha(255 * (i + 1) / mCircleCount);
            //圆心坐标 从最下面的开始画
            canvas.drawCircle(0, mWidth / 2 - mCircleWidth, mCircleWidth, mPaint);
            canvas.rotate(360 / mCircleCount);
        }
    }


    private void initAnimator() {
        mValueAnimator = ValueAnimator.ofInt(0, mCircleCount - 1);
        mValueAnimator.addUpdateListener(mAnimatorUpdateListener);
        mValueAnimator.setDuration(500);
        mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
        mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        mValueAnimator.setInterpolator(new LinearInterpolator());
    }

    //加上旋转动画
    private void startAnimate() {
        if (mValueAnimator == null) {
            initAnimator();
        }
        if (!mValueAnimator.isStarted()) {
            mValueAnimator.start();
        }
    }

    private void stopAnimate() {
        if (mValueAnimator != null) {
            mValueAnimator.removeUpdateListener(mAnimatorUpdateListener);
            mValueAnimator.removeAllUpdateListeners();
            mValueAnimator.cancel();
            mValueAnimator = null;
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        startAnimate();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopAnimate();
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        if (visibility == VISIBLE) {
            startAnimate();
        } else {
            stopAnimate();
        }
    }
}

里面的自定义属性就不贴了,没什么难度。

总结:canvas.translate 和 rotate 是相当好用,不然还要递归去计算坐标,想想头也是大了,现在只用找到一个最好找的 直接旋转画布就搞定了。

你可能感兴趣的:(android)