旋转,聚合,水波纹
最终效果如下图
实现步骤:
- 绘制6个小圆圈,小圆圈是围绕着一个大圆平均绘制的,我们可以计算大圆的总弧度,然后除以小圆的个数,就能得到每个小圆之间间隔的弧度。然后循环绘制
1
2
3
4
5
6
7
8
9
10
11
12
13private 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23private 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
21private 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
15private 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
11protected 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);
}
}
到此动画完成。