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

旋转,聚合,水波纹

最终效果如下图

实现步骤:

  1. 绘制6个小圆圈,小圆圈是围绕着一个大圆平均绘制的,我们可以计算大圆的总弧度,然后除以小圆的个数,就能得到每个小圆之间间隔的弧度。然后循环绘制
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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. 让着些小圆转起来,那就需要通过属性动画了,通过属性动画,动态的改变弧度的大小,就能该表小球的原点坐标了,最后重绘即可。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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,它的意思是向前甩一定值后再回到原来位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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到屏幕对角线的一半。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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。

1
2
3
4
5
6
7
8
9
10
11
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);
}
}

到此动画完成。

源码位置请点这里

# UI

コメント

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×