Android UI程序员Android开发

仿微博头像转圈加载

2018-02-01  本文已影响290人  lonamessi

先来看一下效果图吧


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

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

0-360度

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

360-720度

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

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

<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,它有五个子类

LiearGradient

LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode 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
上一篇下一篇

猜你喜欢

热点阅读