引言
在Android开发中,Canvas为我们提供了一个强大的绘图工具。本文通过分析一个自定义View组件SplashView,展示如何利用Canvas实现小球旋转、扩散聚合和水波纹动画效果,并搭配关键代码片段解释实现细节。
一、效果演示
以下是动画的连贯流程:
旋转阶段:多个彩色小球围绕中心旋转。
扩散聚合:小球向外扩散后聚拢。
水波纹:中心出现逐渐扩大的透明圆洞,背景颜色变化。
二、小球旋转动画
旋转动画通过不断改变小球的绘制角度实现。以下是核心代码:
private class RotateState extends SplashState {
private RotateState() {
mValueAnimator = ValueAnimator.ofFloat(0, (float) (Math.PI * 2));
mValueAnimator.setRepeatCount(2);
mValueAnimator.setDuration(mRotateDuration);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(animation -> {
mCurrentRotateAngle = (float) animation.getAnimatedValue();
invalidate();
});
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mState = new MergeState();
}
});
mValueAnimator.start();
}
@Override
void drawState(Canvas canvas) {
//绘制背景
drawBackground(canvas);
//绘制6个小球
drawCircles(canvas);
}
}
关键点:
通过三角函数(Math.cos和Math.sin)计算每个小球的位置。
三、扩散聚合动画
扩散聚合效果通过动态改变旋转半径实现,使用ValueAnimator驱动动画:
// 扩散聚合状态(MergeState类)
private class MergeState extends SplashState {
private ValueAnimator mExpandAnimator;
public MergeState() {
// 创建扩散动画(半径从小变大)
mExpandAnimator = ValueAnimator.ofFloat(mCircleRadius, mRotateRadius);
mExpandAnimator.setDuration(mRotateDuration);
mExpandAnimator.addUpdateListener(animation -> {
mCurrentRotateRadius = (float) animation.getAnimatedValue();
invalidate();
});
// 动画结束后反向播放(聚合效果)
mExpandAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mExpandAnimator.reverse();
}
});
mExpandAnimator.start();
}
@Override
public void drawState(Canvas canvas) {
drawBackground(canvas);
drawCircles(canvas, mCurrentRotateAngle, mCurrentRotateRadius);
}
}
关键点:
ValueAnimator驱动半径从初始值到目标值的变化。
通过reverse()实现反向动画,形成“扩散-聚合”循环。
四、水波纹动画
水波纹效果通过绘制逐渐扩大的“空洞”实现,结合背景颜色变化:
// 水波纹状态(ExpandState类)
private class ExpandState extends SplashState {
private ValueAnimator mHoleAnimator;
public ExpandState() {
mHoleAnimator = ValueAnimator.ofFloat(0, mDistance);
mHoleAnimator.setDuration(mRotateDuration);
mHoleAnimator.addUpdateListener(animation -> {
mCurrentHoleRadius = (float) animation.getAnimatedValue();
invalidate();
});
mHoleAnimator.start();
}
@Override
public void drawState(Canvas canvas) {
drawBackground(canvas);
}
}
// 绘制背景(带空洞)
private void drawBackground(Canvas canvas) {
if (mCurrentHoleRadius > 0) {
float strokeWidth = mDistance - mCurrentHoleRadius;
float radius = strokeWidth / 2 + mCurrentHoleRadius;
mHolePaint.setStrokeWidth(strokeWidth);
canvas.drawCircle(mCenterX, mCenterY, radius, mHolePaint);
} else {
canvas.drawColor(mBackgroundColor);
}
}
四、动画状态切换
通过状态模式管理不同动画阶段:
// 抽象状态类
private abstract class SplashState {
public abstract void drawState(Canvas canvas);
}
// 动画结束后的状态切换逻辑
mExpandAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mSplashState = new ExpandState(); // 切换到水波纹状态
}
});
五、总结
通过Canvas和属性动画的配合,我们实现了三种连贯的动画效果:
小球旋转:基于角度动态计算位置。
扩散聚合:通过ValueAnimator驱动半径变化。
水波纹:逐渐扩大圆圈,露出原本底色。
完整代码已托管至Gitcode:GitCode - 全球开发者的开源社区,开源代码托管平台
希望本文能为你实现复杂Canvas动画提供启发!
通过代码片段与文字的结合,读者可以更直观地理解每个动画的实现方式。如果需要进一步解释某个代码细节,可以随时补充!