Android动画篇(一):圆形进度条CircleProgressBar

前言#

最近看框架和源码比较多,很久没有写动画了,相信很多的朋友都对动画感兴趣,我也不例外,毕竟做前端还是要靠动画特效吃饭的,并且比写功能模块更有成就感。

今天我们就来个稍微简单一点的CircleProgressBar热个身。

首先需要对ValueAnimator动画,还有Canvas,Paint画图的相关的类和API都有一定的了解,所以这部分还比较薄弱的朋友可以先去学习一下基础知识,否则可能会有些吃力。

正文#

先看一下效果图,我不会录屏,就百度了一张图片:

这里写图片描述

大概是这样的效果,首先我们不考虑效果,先画出这个圆形的进度条,新建文件CircleProgressBar:

/**
     * 进度
     */
    private float mProgress = 50;

    /**
     * 最大进度
     */
    private int mMaxProgress = 100;

/**
     * 绘制进度条
     */
    private void drawProgress(Canvas canvas) {
        // 开始画进度条
        // 首先画出背景圆
        mPaint.setColor(mBackgroundColor);
        mPaint.setStyle(Paint.Style.FILL);
        // 这里减去了边框的宽度
        canvas.drawCircle(mWidth / 2, mHeight / 2, mRadius - mBorderWidth, mPaint);
        // 画出进度条
        mPaint.setColor(mProgressBorderColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mBorderWidth);

        // 计算圆弧划过的角度
        float angle = CIRCULAR / mMaxProgress * mProgress;
        // 这里要画圆弧
        canvas.drawArc(mContentRectF, -90, angle, false, mPaint);
        // 画出补全部分的进度条
        mPaint.setColor(mBorderColor);
        mPaint.setStrokeWidth(mBorderWidth);
        // 这里要画圆弧
        canvas.drawArc(mContentRectF, -90 + angle, CIRCULAR - angle, false, mPaint);
    }

先话出背景色的圆,然后画出进度条的颜色的边框,再画出进度条以外的部分,为了显示的明显,我分别用了三个颜色,最终的效果:

这里写图片描述

最初的样子已经出来了,但是有一个小细节要注意:

这里贴出mMaxProgress = 100,为什么不是1000,10000呢?当然也可以,但是我不推荐这个数字过大,大家可以去看看系统自带的ProgressBar,他的注释有提醒开发者,不要使用过大的max,最好是100,感兴趣的可以去看一看。

现在就差动画了,接下来我们来分析一下动画:

1、首先进度会飞快的上涨,以顺时针为方向,伸长的部分是头部。
2、然后进度会飞速的下降,以顺时针为方向,缩短的部分是尾部。

首先我们来完成第一部分:

/**
     * 开始过度动画
     */
    private void startIntermediateAnim() {
        if (valueAnimator == null) {
            valueAnimator = new ValueAnimator().ofFloat(0, mMaxProgress);
            valueAnimator.setDuration(DURATION);
            valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                // 设置进度
                    float value = (float) valueAnimator.getAnimatedValue();
                    setProgress(value);
                }

            });
        valueAnimator.setRepeatCount(-1);
        valueAnimator.start();
    }

也是没什么太多的技术含量,但是有几点需要说明一下:

我们使用ValueAnimator().ofFloat,是为了动画的流畅性,如果你使用了int,你会发现动画会有一些细微的卡顿,因为int型舍弃了小数部分,这样就会出现误差,视觉上就会出现卡顿。

伸长的动画已经成型了,那缩短的动画不就简单了,直接动画reverse不就好了?我激动得设置了:

valueAnimator = new ValueAnimator().ofFloat(0, mMaxProgress,0);

或者是

valueAnimator.setRepeatMode(ValueAnimator.REVERSE);

我迫不及待的运行了代码,卧草草,竟然不行?仔细观察效果,我们发现了问题:

如果是缩短,还是从顺时针的头部缩短,而不是从尾部,这是为什么呢?其实从api也可以理解,因为我们是从起始位置开始画弧,只要起始位置不变,尾部肯定不会发生变化。

虽然明白了这个道理,但是心情非常的压抑,难道就要放弃了?突然灵光一闪,我发现了一个神奇的办法,不知道机智的小伙伴是不是也想到了:

还记得我之前的绘图步骤吗?
先绘制进度部分,然后剩余部分不全。

既然进度部分只能头部伸长,尾部也是一样,那我让补全部分伸长,那进度部分的尾部不就是缩短了吗?

那如何让补全部分伸长呢?

1、把progress在伸长结束时,开始让补全部分使用progress,从而让他伸长,但是这样会改变原有的功能逻辑,非常危险,工作量也大。

2、最简单的办法,把进度的颜色不补全部分的颜色交换,然后把位置也互换,不就OK了?

经过简单的修改之后:

if (valueAnimator == null) {
            valueAnimator = new ValueAnimator().ofFloat(0, mMaxProgress);
            valueAnimator.setDuration(DURATION);
            valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    float value = (float) valueAnimator.getAnimatedValue();
                    setProgress(value);
                }

            });
            valueAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {

                }

                @Override
                public void onAnimationEnd(Animator animator) {
            // 因为是循环动画,所以这里不会回调
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {
                    // 互换两者的颜色
                    int color = getProgressBackgroundColor();
                    setProgressBackgroundColor(getProgressColor());
                    setProgressColor(color);
                }
            });
        }
        valueAnimator.setRepeatCount(-1);
        valueAnimator.start();

ok,还有谁?最关键的部分已经全部完成了,还差最后一点点,仔细的观察效果图,发现进度条是有最小进度的,没有完全消失,所以我们再设置一个最小进度,并且每次设置进度的时候,我们都稍微旋转一下角度,这样就会一边伸长缩短一边旋转了:

/**
     * 开始过度动画
     */
    private void startIntermediateAnim() {
        if (valueAnimator == null) {
            valueAnimator = new ValueAnimator().ofFloat(mMinProgress, mMaxProgress - mMinProgress);
            valueAnimator.setDuration(DURATION);
            valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    float value = (float) valueAnimator.getAnimatedValue();
                    setProgress(value);
                    // 每次旋转2度
                    mStartAngle += 2;
                }

            });
            valueAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {

                }

                @Override
                public void onAnimationEnd(Animator animator) {

                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {
                // 因为有了最小进度,所以每次都要位置设置到补全部分的位置
                    mStartAngle = mStartAngle - CIRCULAR / mMaxProgress * mMinProgress;
                    // 互换两者的颜色
                    int color = getProgressBackgroundColor();
                    setProgressBackgroundColor(getProgressColor());
                    setProgressColor(color);
                }
            });
        }
        valueAnimator.setRepeatCount(-1);
        valueAnimator.start();
    }

最终的效果,就想一开始的效果图一样,这里不贴了。

总结#

看上去稍微有点复杂的动画,经过我们的分析拆解,就变得很简单了。如果是利用Translation, Rotate 这样的动画,那实现起来真是太难了,ValueAnimator就是从他们中分离出来的专门用来计算差值的强大武器,有了它我们开发一些高级的效果,就简单多了。

我对demo进行了一些修改,即可以是普通的圆形进度条,也可以是loading的动画,就想progressBar一样,大家可以下载下来,参考一下。

github下载地址

ok,今天就到这里了,明天就是周末了,祝大家浪起来~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容

  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,701评论 0 10
  • Animation Animation类是所有动画(scale、alpha、translate、rotate)的基...
    四月一号阅读 1,915评论 0 10
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,008评论 25 707
  • 内心恐惧这事吧,你有我有大家有。 其实我也会一度担心说,有一天你就类似我和老妈闲聊时那般,突然自我醒悟要决断,然后...
    暖渣男的等待阅读 279评论 2 0
  • 他们都嘲笑我, 嘲笑我对性的保守,嘲笑我 还是处子之身。 他们都嘲笑我, 嘲笑我不够吵闹, 把疯狂抑制在心坎。 他...
    新新月文艺阅读 253评论 2 8