引言
在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() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);mState = new MergeState();}});mValueAnimator.start();}@Overridevoid 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() {@Overridepublic void onAnimationEnd(Animator animation) {mExpandAnimator.reverse();}});mExpandAnimator.start();}@Overridepublic 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();}@Overridepublic 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() {@Overridepublic void onAnimationEnd(Animator animation) {mSplashState = new ExpandState(); // 切换到水波纹状态}
});
五、总结
通过Canvas和属性动画的配合,我们实现了三种连贯的动画效果:
小球旋转:基于角度动态计算位置。
扩散聚合:通过ValueAnimator驱动半径变化。
水波纹:逐渐扩大圆圈,露出原本底色。
完整代码已托管至Gitcode:GitCode - 全球开发者的开源社区,开源代码托管平台
希望本文能为你实现复杂Canvas动画提供启发!
通过代码片段与文字的结合,读者可以更直观地理解每个动画的实现方式。如果需要进一步解释某个代码细节,可以随时补充!