仿微博头像转圈加载

先来看一下效果图吧


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
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 179,443评论 25 708
  • <转>我也忘了转自哪里,抱歉,感谢原作者 什么是Shader Shader(着色器)是一段能够针对3D对象进行操作...
    星易乾川阅读 5,878评论 1 16
  • 19号的工作安排的有些松散,预留了下午两个小时和晚上的时间。因此下午办完自己的事情后遇到了一个不相关的人和不...
    努力中的陈子君阅读 268评论 0 0
  • 兴坪湖奇山异峰 银子岩鬼斧神工 桂林山水甲天下 阳朔山水甲桂林 好山好景好风光 人山人海夕阳红 诗词歌赋遍地飘 一...
    微光_71b8阅读 137评论 0 0
  • 昨天,外面风吹得簌簌响。风并不大,但我能想到树叶漫天飞舞各自飘零的样子。现在,可正是落叶的季节。 十年之前...
    Today便利店阅读 591评论 0 0

友情链接更多精彩内容