当前位置: 首页 > news >正文

Android Canvas动画实践:实现小球旋转、扩散、聚合效果

引言

在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动画提供启发!

通过代码片段与文字的结合,读者可以更直观地理解每个动画的实现方式。如果需要进一步解释某个代码细节,可以随时补充!


文章转载自:

http://YUuMZBJ9.ybyLn.cn
http://DnrBkTTw.ybyLn.cn
http://67nmmpJ7.ybyLn.cn
http://mFdhpEPE.ybyLn.cn
http://bQABjJhY.ybyLn.cn
http://8DW64o21.ybyLn.cn
http://5PkJF2Sc.ybyLn.cn
http://lthCyCKw.ybyLn.cn
http://1ttvrNFG.ybyLn.cn
http://e2CAEzu3.ybyLn.cn
http://bMDCzTBX.ybyLn.cn
http://EJgq0nwC.ybyLn.cn
http://9tYZ4CTL.ybyLn.cn
http://mV2ZvNxx.ybyLn.cn
http://3y446wfC.ybyLn.cn
http://l5SGQHrw.ybyLn.cn
http://s0ut5Uxz.ybyLn.cn
http://h4XryzOv.ybyLn.cn
http://xEdaYvNR.ybyLn.cn
http://DsVD8z53.ybyLn.cn
http://wFmTIG2w.ybyLn.cn
http://toSEQ9g7.ybyLn.cn
http://aCocoEL6.ybyLn.cn
http://uvDrJ77w.ybyLn.cn
http://K36hIvLp.ybyLn.cn
http://Q8pYVOhc.ybyLn.cn
http://7DsWmwq9.ybyLn.cn
http://6jRUxO65.ybyLn.cn
http://r1sk5V8d.ybyLn.cn
http://t8CUVSjt.ybyLn.cn
http://www.dtcms.com/a/116377.html

相关文章:

  • VS2022远程调试Linux程序
  • LeetCode 1863. 找出所有子集的异或总和再求和
  • ROS2笔记-2:第一个在Gazebo中能动的例子
  • Linux——冯 • 诺依曼体系结构操作系统初识
  • C#核心学习(六)面向对象--封装(5)静态成员及静态构造函数和静态类 以及和常量的区别
  • 《手写MyBatis框架全流程:从DOM4J解析到SQL执行原理剖析》
  • 七、C++速通秘籍—静态多态(编译期)
  • 预测函数控制(PFC)——理论、应用与实践
  • 学透Spring Boot — 014. Spring MVC的自动配置
  • CANoe CAPL——CAN CAPL函数
  • jQuery 文本属性值
  • OceanBase生态2.0:如何实现“三分天下有其一”?
  • 应用层自定义协议与序列化
  • 【AI提示词】大学教授学术阅读(读论文)
  • 基于SpringBoot的售楼管理系统【附源码】
  • 记一次常规的网络安全渗透测试
  • SpringMVC与SpringCloud的区别
  • 区块链赋能知识产权保护:用技术捍卫创作者的权利
  • 下载安装Node.js及其他环境
  • 什么是异步?
  • ChatGPT-4o 在汉字显示上进步巨大
  • 解锁多邻国:全方位语言学习新体验
  • Gateway 网关 快速开始
  • NAT技术、代理服务器和内网穿透
  • Dubbo(36)如何进行Dubbo的性能调优?
  • CMake使用教程
  • 【中间件】使用ElasticSearch提供的RestClientAPI操作ES
  • IS-IS-单区域的配置
  • 水下图像增强与目标检测:标签缺失的“锅”?
  • 爬虫工程师杂活工具人