Canvas和属性动画实现好看的效果

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mingyunxiaohai/article/details/88916801

最终效果如下图
在这里插入图片描述

实现步骤:

  1. 绘制6个小圆圈,小圆圈是围绕着一个大圆平均绘制的,我们可以计算大圆的总弧度,然后除以小圆的个数,就能得到每个小圆之间间隔的弧度。然后循环绘制
   private void drawSmallCircle(Canvas canvas) {
        canvas.drawColor(Color.WHITE);
        //一个完整的圆的弧度是2π,计算每个小圆之间的弧度
        float angleEach = (float) (2*Math.PI/mCircleColors.length);
        //循环绘制6个小圆
        for (int i = 0; i < mCircleColors.length; i++) {
            float angle = i*angleEach+mCurrentAngle;
            float x = (float) (Math.cos(angle)*mRotateRadius+mCenterX);
            float y = (float) (Math.sin(angle)*mRotateRadius+mCenterY);
            mPaint.setColor(mCircleColors[i]);
            canvas.drawCircle(x,y,mCircleRadius,mPaint);
        }
    }

小圆的的原点的坐标可以看下图,根据我们中学数学中所讲,x=sin(角度)*大圆的半径,y=cos(角度)*大圆的半径。最后在加上大圆的原点坐标,就是上面代码上中的计算方法了。
在这里插入图片描述

  1. 让着些小圆转起来,那就需要通过属性动画了,通过属性动画,动态的改变弧度的大小,就能该表小球的原点坐标了,最后重绘即可。
    private void initRotateAnimator() {
        //弧度从0到最大弧度
        ValueAnimator rotateAnimator = ValueAnimator.ofFloat(0, (float) (Math.PI * 2));
        rotateAnimator.setDuration(1000);
        rotateAnimator.setRepeatCount(2);
        rotateAnimator.setInterpolator(new LinearInterpolator());
        rotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentAngle = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        //监听动画结束,然后开启另一个动画
        rotateAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                initAggregationAnimator();
            }
        });
        rotateAnimator.start();
    }

3.让小球执行聚合扩展的动画,首先我们动态的改变大圆的半径值,然后重绘。这里需要设置一个特殊的插值器OvershootInterpolator,它的意思是向前甩一定值后再回到原来位置。

    private void initAggregationAnimator() {
        ValueAnimator aggregationAnimator = ValueAnimator.ofFloat(mCircleRadius, mRotateRadius);
        aggregationAnimator.setDuration(500);
        aggregationAnimator.setInterpolator(new OvershootInterpolator(10f));
        aggregationAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mRotateRadius = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        //监听动画结束,然后开启另一个动画
        aggregationAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                initRippleAnimator();
            }
        });
        aggregationAnimator.reverse();
    }

上面的两个动画中用到了两个插值器:LinearInterpolator和OvershootInterpolator。Android系统中默认给我们提供了很多插值器如下:

插值器 描述
AccelerateDecelerateInterpolator 开始和结束缓慢,中间加速
AccelerateInterpolator 在动画开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator 开始的时候向后甩一下,然后向前
AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator 动画结束的时候弹起
CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator 开始的时候快,结束的时候慢
LinearInterpolator 以常量速率变化
OvershootInterpolator 运动到终点后,冲过终点后再回弹

4.最后绘制一个水波纹的效果,可以通过绘制空心圆的做法实现,通过属性动画,动态改变圆的半径,圆的半径从0到屏幕对角线的一半。

    private void initRippleAnimator() {
    //mDistance为屏幕对角线的一半
        ValueAnimator rippleAnimator = ValueAnimator.ofFloat(mCircleRadius, mDistance);
        rippleAnimator.setDuration(1200);
        rippleAnimator.setInterpolator(new LinearInterpolator());
        rippleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                isRipple = true;
                mHollowRadius = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        rippleAnimator.start();
    }

最后是onDraw方法中,因为前两部需要绘制6个小圆,最后一步绘制一个空心圆不用绘制小圆,通过一个标志位isRipple来判断需要绘制哪个。标志位在第二个动画结束的时候置为true。

  protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(isRipple){
            float strokeWidth = (mDistance - mHollowRadius);
            mHollowPaint.setStrokeWidth(strokeWidth);
            float radius = strokeWidth / 2 + mHollowRadius;
            canvas.drawCircle(mCenterX,mCenterY,radius,mHollowPaint);
        }else {
            drawSmallCircle(canvas);
        }
    }

到此动画完成。

源码位置请点这里

猜你喜欢

转载自blog.csdn.net/mingyunxiaohai/article/details/88916801