仿微博头像转圈加载

作者: lonamessi | 来源:发表于2018-02-01 14:02 被阅读290次

    先来看一下效果图吧


    2018-01-31-20-24-45.gif

    我们在自定义一个控件的时候首先要分析它的结构。
    可以看出一共有四部分四个圆弧,两个线条圆弧,两个点状圆弧绘画的步骤分别是

    0-360度

    1.从0点开始,角度从360度到0度的一个线条圆
    2.一小段一小段的弧线,然后让画布旋转绘制成圆弧。

    360-720度

    3.从0点开始角度从0-360度的一个线条圆
    4.一小段一小段的弧线,然后让画布旋转绘制成圆弧。

    首先我们自定义一个控件,需要我们先分析它的属性,然后定义自定义属性:

    • mStartColor 开始的颜色(因为是渐变样式)
    • mEndColor 结束的颜色(因为是渐变样式)
    • mArcRadian 一小段弧线的弧度
    • mArcWidth 画弧线的宽度
    • mArcSpacing 每一段弧线的距离
      在values下创建attrs.xml文件
    <resources>
        <declare-styleable name="LoradingHeard">
            <attr name="circleStartColor" format="color|reference"/>
            <attr name="circleEndColor" format="color|reference"/>
            <attr name="circleArcWidth" format="integer|reference"/>
            <attr name="circleArcRadian" format="integer|reference"/>
            <attr name="circleArcSpacing" format="integer|reference"/>
        </declare-styleable>
    </resources>
    

    declare-styleable: 表示一个属性组。它的name必须和你自定义view的名字相同。
    获取自定义属性的方法我们可以通过
    getContext().obtainStyledAttributes()获取TypedArray,
    通过TypedArray来获取自定义属性的值,获取完成之后,我们必须要用typedArray 的recycle()的方法将 TypedArray 回收。

    mStartColor = typedArray.getColor(R.styleable.LoradingHeard_circleStartColor,
                   ContextCompat.getColor(context, R.color.colorPrimary));
    
    mEndColor = typedArray.getColor(R.styleable.LoradingHeard_circleEndColor,
                   ContextCompat.getColor(context, R.color.colorPrimaryDark));
    
    mArcSpacing = typedArray.getInteger(R.styleable.LoradingHeard_circleArcSpacing, 10);
    
    mArcRadian = typedArray.getInteger(R.styleable.LoradingHeard_circleArcRadian, 5);
    
    mArcWidth = typedArray.getInteger(R.styleable.LoradingHeard_circleArcWidth, 5);
     typedArray.recycle();
    

    我们可以看到在获取玩xml的自定义属性以后调用了recycle(),这个方法,那么为什么要调用这个方法?
    首先我们来看一下typedArray的创建

     @NonNull
            TypedArray obtainStyledAttributes(@NonNull Resources.Theme wrapper,
                    AttributeSet set,
                    @StyleableRes int[] attrs,
                    @AttrRes int defStyleAttr,
                    @StyleRes int defStyleRes) {
                synchronized (mKey) {
                    final int len = attrs.length;
                    final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
                     其他代码
                    return array;
                }
            }
    

    可以看出来并不是实例化出来的,是调用了TypedArray的静态方法来获得typedArray的实例的。但是到这里还是没能解决我们的问题,那就接着往下看TypedArray.obtain的方法:

       static TypedArray obtain(Resources res, int len) {
            TypedArray attrs = res.mTypedArrayPool.acquire();
            if (attrs == null) {
                attrs = new TypedArray(res);
            }
    
            attrs.mRecycled = false;
            // Reset the assets, which may have changed due to configuration changes
            // or further resource loading.
            attrs.mAssets = res.getAssets();
            attrs.mMetrics = res.getDisplayMetrics();
            attrs.resize(len);
            return attrs;
        }
    

    我们发现内部是一个单利的形式,发现typedArray这个实例是从线程池里面获取的。这样我们是不是就知道了,其实程序在运行时维护了一个 TypedArray的池,需要的时候我们会向线程池请求实例,用完后调用recycle()方法释放这个实例,从而让其可以被复用,而不是重新创建,这样就减少了系统频繁创建array,导致oom。

    Shader

    从效果图中可以看出来整个线条圆是分为渐变的颜色。
    如果想画出渐变的效果我们可以用这个类 Shader,它有五个子类

    • BitmapShader : 位图绘制时纹理的着色器
    • ComposeShader :组合着色器
    • LinearGradient :线性渐变着色器(我们要用到这个)
    • RadialGradient : 径向渐变着色器
    • SweepGradient :绕着一个中心点进行扫描的渐变着色器
      一般用再paint.setShader(shader)中,shader是一个Shader对象。
    LiearGradient

    LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)

    • x0表示渐变的起始点x坐标
    • y0表示渐变的起始点y坐标
    • x1表示渐变的终点x坐标
    • y1表示渐变的终点y坐标
    • color0表示渐变开始颜色
    • color1表示渐变结束颜色
    • tile表示平铺模式
     Shader shader = new LinearGradient(0f, 0f, mWidth, mHeight,
                    mStartColor, mEndColor, Shader.TileMode.CLAMP);
    paint.setShader(shader);
    
    现在我们的所有准备活动都准备完毕,那就开始画吧上核心代码
    A:第一个圆弧从0点开始,角度从360度到0度
      if (maxAngle <= 360) {
                float angle = 0;
                canvas.rotate(mRatio * maxAngle / 360, mWidth / 2, mHeight / 2);
                canvas.drawArc(mRectF, maxAngle, 360 - maxAngle, false, paint);
    B:一小段弧线,然后让画布旋转360度
                while (angle <= maxAngle) {
                    float length = mArcRadian * angle / maxAngle;
                    canvas.drawArc(mRectF, 0, length, false, paint);
                    canvas.rotate(mArcSpacing, mWidth / 2, mHeight / 2);
                    angle += mArcSpacing;
                }
    C::第一个圆弧从0点开始,角度从度到0度360度。
            } else {
                float angle = 0;
                canvas.rotate(mRatio + mRatio * (maxAngle - 360) / 360, mWidth / 2, mHeight / 2);
                canvas.drawArc(mRectF, 0, maxAngle - 360, false, paint);
                canvas.rotate(maxAngle - 360, mWidth / 2, mHeight / 2);
    D:一小段弧线,然后让画布旋转360度
                while (angle <= 720 - maxAngle) {
                    float length = mArcRadian * angle / (720 - maxAngle);
                    canvas.drawArc(mRectF, 0, length, false, paint);
                    canvas.rotate(mArcSpacing, mWidth / 2, mHeight / 2);
                    angle += mArcSpacing;
                }
            }
    E:刷新数据进行更新绘制
            if (maxAngle <= 720) {
                maxAngle += mArcSpacing;
                postInvalidateDelayed(30);
            }
    
    相关代码链接:https://github.com/gengyaping/CustomView

    相关文章

      网友评论

      本文标题:仿微博头像转圈加载

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