美文网首页Android自定义ViewAndroid开发经验谈Android开发
Android动画篇(一):圆形进度条CircleProgres

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

作者: 珠穆朗玛小王子 | 来源:发表于2017-09-13 10:32 被阅读95次

    前言#

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

    今天我们就来个稍微简单一点的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,今天就到这里了,明天就是周末了,祝大家浪起来~

    相关文章

      网友评论

        本文标题:Android动画篇(一):圆形进度条CircleProgres

        本文链接:https://www.haomeiwen.com/subject/hgacsxtx.html