仿微博头像转圈加载

作者: 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