Android 自定义圆形旋转进度条,仿微博头像加载效果

微博 App 的用户头像有一个圆形旋转进度条的加载效果,看上去效果很是不错,如图所示:java

听说 Instagram 也采用了这种效果。最近抽空研究了一下,最后实现的效果是这样:android

基本上能模拟出个大概,代码量不大,来说讲实现思路吧。git

模拟一种动画效果,首先须要仔细分析其运做过程,找到其中的物理规律,从而肯定实现方案。像这种运动速度较快的动画,通常不是很容易看清。能够先经过录屏软件,录取动画运做的过程,而后借助一些辅助工具放慢放大,好比 PS,反复重复播放几遍,基本上就能看出动画的运做规律了。程序员

回到这里的加载效果,拆分开来,能够理解为画笔上的两层绘制和时间上的两段过程。时间上,很明显能够看出分为前 360 度和后 360 度,主要在画笔上:github

1,单一完整的圆弧绘制。前 360 度,从 360 度的圆弧到 0 度圆弧的递减过程;后 360 度,从 0 度圆弧到 360 度圆弧的递增过程。canvas

2,重复片断的圆弧绘制。前 360 度,从零开始,逐渐递增,直到多段填满圆周;后 360 度,反过来,逐渐递减,直到数量为零。微信

其余的就是细节的处理,后面具体实现时再说起,咱们先来看看如何实现这两个核心流程的绘制。app

因为这两个流程使用的画笔属性相同,因此使用一个 Paint 对象便可,这段代码没什么好讲的,就是一些初始化工做:ide

mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mArcWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);复制代码

注意,与微博原图效果不一样的是,个人效果图中使用到了渐变色圆环,这样效果更好看一些。使用 setShader() 方法能够给画笔设置渐变色,实现方式是:函数

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    ......

    Shader shader = new LinearGradient(0f, 0f, mWidth, mHeight,
            mStartColor, mEndColor, Shader.TileMode.CLAMP);
    mPaint.setShader(shader);
}复制代码

Shader 子类有不少,这里使用的是线性渐变类 LinearGradient,因为须要使用 View 宽高,因此放在了 onSizeChanged() 函数里面。

核心计算和绘制工做都在 onDraw() 方法里面,看下这里的代码:

@Override
protected void onDraw(Canvas canvas) {
    if (maxAngle <= 360) {
        float angle = 0;
        canvas.rotate(mRatio * maxAngle / 360, mWidth / 2, mHeight / 2);
        canvas.drawArc(mRectF, maxAngle, 360 - maxAngle, false, mPaint);
        while (angle <= maxAngle) {
            float length = mArcRadian * angle / maxAngle;
            canvas.drawArc(mRectF, 0, length, false, mPaint);
            canvas.rotate(mArcSpacing, mWidth / 2, mHeight / 2);
            angle += mArcSpacing;
        }
    } else {
        float angle = 0;
        canvas.rotate(mRatio + mRatio * (maxAngle - 360) / 360, mWidth / 2, mHeight / 2);
        canvas.drawArc(mRectF, 0, maxAngle - 360, false, mPaint);
        canvas.rotate(maxAngle - 360, mWidth / 2, mHeight / 2);
        while (angle <= 720 - maxAngle) {
            float length = mArcRadian * angle / (720 - maxAngle);
            canvas.drawArc(mRectF, 0, length, false, mPaint);
            canvas.rotate(mArcSpacing, mWidth / 2, mHeight / 2);
            angle += mArcSpacing;
        }
    }

    if (maxAngle <= 720) {
        maxAngle += mArcSpacing;
        postInvalidateDelayed(30);
    }
}复制代码

前面提到,动画在时间上分先后 360 度的两段过程,因此这里定义了一个 maxAngle 变量来定义时间的变化。能够看到,先后 360 度的绘制代码看上去差很少,但仍是有很大区别的。须要理解的地方有:

1,多个小段圆弧能够利用画布旋转的方式轻松实现,也就是 canvas.rotate() 方法,上面代码中的 while 循环部分。这里有个细节处理,就是每段圆弧的弧度有个递增变化。

2,整个 View 给人的感受有一种旋转的效果,为了实现这个效果,在每次绘制前,都增长了旋转的步骤,也就是这行代码:

canvas.rotate(mRatio * maxAngle / 360, mWidth / 2, mHeight / 2);复制代码

其中 mRatio 表示整个动画结束时,View 相比初始状态时总体旋转的角度。 这里我设置的默认值是 60,值越大,动画执行时呈现出越快的旋转效果。你们能够修改源码,本身尝试一下。

3,还有一点就是,maxAngle 变量每次增量值的设置,必定要设置为相邻两段片断圆弧的间距弧度。这样作的目的是,保证每次绘制,片断圆弧都能有一个完整的递增或递减。不然,动画执行时,看上会发生视觉上的抖动效果。

基本上就是这样,代码量虽然很少,可是各类细节的处理仍是耗费了不少时间调整。原本想使用一些图例展现一下每行代码的做用,可是太费时间了。若是感兴趣的,彻底能够本身下载一下源码,修改试试看。

通过简单地封装,提取出一部分属性:

<declare-styleable name="CircleLoadingView">
    <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>复制代码

注意:中间的头像部分不是这里自定义 View 的内容,使用时能够自由填充内容。好比,看下我这里的使用方式,仍是比较自由的:

<RelativeLayout android:layout_width="200dp" android:layout_height="200dp" android:layout_centerInParent="true">

    <com.yifeng.view.view.CircleLoadingView android:layout_width="match_parent" android:layout_height="match_parent" app:circleStartColor="#ffff00" app:circleEndColor="#ff0000" app:circleArcRadian="5" app:circleArcWidth="10" app:circleArcSpacing="10"/>

    <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp" android:src="@mipmap/ic_avatar_default"/>

</RelativeLayout>复制代码

源代码仍是老样子,统一在 GitHub 上的自定义 View 集锦库里,地址以下。:

github.com/Mike-bel/an…

关于我:亦枫,博客地址:yifeng.studio/,新浪微博:IT亦枫

微信扫描二维码,欢迎关注个人我的公众号:安卓笔记侠

不只分享个人原创技术文章,还有程序员的职场遐想

相关文章
相关标签/搜索