Android实现漂亮的波纹动画

news2025/5/15 12:07:22

Android实现漂亮的波纹动画

本文章讲述如何使用二维画布canvas和camera、矩阵实现二、三维波纹动画效果(波纹大小变化、画笔透明度变化、画笔粗细变化)

一、UI界面

界面主要分为三部分
第一部分:输入框,根据输入x轴、Y轴、Z轴倾斜角度绘制波纹动画立体效果
第二部分:点击按钮PLAY:开始绘制动画;点击按钮STOP:停止绘制动画
第三部分:绘制波纹动画的自定义view
在这里插入图片描述
activity_main.xml实现如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:gravity="center"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="8dp">
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:gravity="center_horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="X轴倾斜" />
            <EditText
                android:id="@+id/editText_X"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="number"
                android:text="0"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:gravity="center_horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Y轴倾斜" />
            <EditText
                android:id="@+id/editText_Y"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="number"
                android:text="0"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:orientation="vertical"
            android:gravity="center_horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Z轴倾斜" />
            <EditText
                android:id="@+id/editText_Z"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="number"
                android:text="0"/>
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:paddingTop="16dp">

        <Button
            android:id="@+id/playButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Play"
            android:layout_marginEnd="8dp"/>

        <Button
            android:id="@+id/stopButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Stop"
            android:layout_marginEnd="8dp"/>

    </LinearLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_gravity="center">

        <!-- WaveView -->
        <com.example.waveanimationbysingleview.animation.WaveView
            android:id="@+id/wave_view"
            android:layout_width="match_parent"
            android:layout_height="606dp"
            android:layout_gravity="center" />

    </FrameLayout>


</LinearLayout>

二、自定义view实现

新建一个WaveView类继承自View,声明类成员变量和实现构造函数初始化

public class WaveView extends View{

    private RippleAnimationInfo mRippleInfo;//动画属性类,包括最大、最小半径,光圈等
    private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private float centerX, centerY; //绘制画布中心点
    private float[] mRadiusArray;  // 存储当前绘制每圈光波的半径
    private float[] mAlphaArray;   //存储当前绘制每圈光波的透明度
    private float[] mStrokenArray; //存储当前绘制每圈光波的画笔宽度

    private final Camera mCamera = new Camera();//相机
    private final Matrix mMatrix = new Matrix();//矩阵
    private final RectF mRectF = new RectF();
    private Boolean mIsDrawing = false;//是否绘制标志位

    private Bitmap mLogoBitmap;//中心点表情😝logo

    private AnimatorSet mAnimatorSet = new AnimatorSet();//多动画组合类
    private List<Animator> mAnimatorList = new ArrayList<>();//存储动画列表

    public WaveView(Context context) {
        super(context);
        init();
    }

    public WaveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

构造函数初始化

添加动画

  1. 圆圈等比变大动画
  2. 透明度渐变动画
  3. 画笔由细变粗动画
private void initAnimation() {
        int rippleDelay = mRippleInfo.durationTime/mRippleInfo.rippleCount;
        for (int i = 0; i < mRippleInfo.rippleCount; i++) {
            //动画1-半径变大
            final int index = i;
            ValueAnimator radiusAnimator = ValueAnimator.ofFloat(mRippleInfo.minRadius, mRippleInfo.maxRadius);
            radiusAnimator.setDuration(mRippleInfo.durationTime); // 动画持续时间
            radiusAnimator.setStartDelay(i * rippleDelay); // 延迟启动
            radiusAnimator.setRepeatCount(ValueAnimator.INFINITE);
            radiusAnimator.setRepeatMode(ValueAnimator.RESTART);
            radiusAnimator.addUpdateListener(animation -> {
                mRadiusArray[index] = (float) animation.getAnimatedValue();
                invalidate(); // 重绘视图
            });

            //动画2-画笔变粗
            ValueAnimator strokeAnimator = ObjectAnimator.ofFloat(mRippleInfo.minStrokenWidth, mRippleInfo.maxStrokenWidth);
            strokeAnimator.setDuration(mRippleInfo.durationTime); // 动画持续时间
            strokeAnimator.setStartDelay(i * rippleDelay); // 延迟启动
            strokeAnimator.setRepeatCount(ValueAnimator.INFINITE);
            strokeAnimator.setRepeatMode(ValueAnimator.RESTART);
            strokeAnimator.addUpdateListener(animation -> {
                mStrokenArray[index] = (float) animation.getAnimatedValue();
                invalidate(); // 重绘视图
            });
            //动画3-颜色淡出
            ValueAnimator alphaAnimator = ObjectAnimator.ofFloat( 0.1f, 0.8f, 0.8f,0.4f, 0);
            alphaAnimator.setDuration(mRippleInfo.durationTime); // 动画持续时间
            alphaAnimator.setStartDelay(i * rippleDelay); // 延迟启动
            alphaAnimator.setRepeatCount(ValueAnimator.INFINITE);
            alphaAnimator.setRepeatMode(ValueAnimator.RESTART);
            alphaAnimator.addUpdateListener(animation -> {
                mAlphaArray[index] = (float) animation.getAnimatedValue();
                invalidate(); // 重绘视图
            });


            mAnimatorList.add(radiusAnimator);
            mAnimatorList.add(strokeAnimator);
            mAnimatorList.add(alphaAnimator);
        }
        mAnimatorSet.playTogether(mAnimatorList);
    }

应用矩阵变换

重写ondraw

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (!mIsDrawing)
        {return;}

        canvas.save();
        // 计算中心点
        centerX = getWidth() / 2.0f;
        centerY = getHeight() / 2.0f;

        canvas.concat(mMatrix);// 将矩阵应用到画布上
        //绘制多圈波纹
        for (int i = 0; i < mRippleInfo.rippleCount; i++) {
            if (mRadiusArray[i] > 0) {
                paint.setStrokeWidth(mStrokenArray[i]);
                paint.setAlpha((int)(255 * mAlphaArray[i]));
                canvas.drawCircle(centerX, centerY,mRadiusArray[i],paint);
            }
        }
        // 中心点绘制 logo
        canvas.drawBitmap(mLogoBitmap, (getWidth()- mRippleInfo.minRadius)/2, (getHeight()- mRippleInfo.minRadius)/2, null);
        canvas.restore();
    }

二、二维波纹动画

waveAnimation_2D

三、三维波纹动画

waveAnimation_3D

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2308162.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【无标题】ABP更换MySql数据库

原因&#xff1a;ABP默认使用的数据库是sqlServer&#xff0c;本地没有安装sqlServer&#xff0c;安装的是mysql&#xff0c;需要更换数据库 ABP版本&#xff1a;9.0 此处以官网TodoApp项目为例 打开EntityFrameworkCore程序集&#xff0c;可以看到默认使用的是sqlServer&…

Windows上使用go-ios实现iOS17自动化

前言 在Windows上运行iOS的自动化&#xff0c;tidevice对于iOS17以上并不支持&#xff0c;原因是iOS 17 引入新通信协议 ‌RemoteXPCQUIC‌&#xff0c;改变了 XCUITest 的启动方式。 一、go-ios的安装 1、安装命令&#xff1a;npm i go-ios 2、安装完成后输入命令which io…

Grok3使用体验与模型版本对比分析

文章目录 Grok的功能DeepSearch思考功能绘画功能Grok 3的独特功能 Grok 3的版本和特点与其他AI模型的比较 最新新闻&#xff1a;Grok3被誉为“地球上最聪明的AI” 最近&#xff0c;xAI公司正式发布了Grok3&#xff0c;并宣称其在多项基准测试中展现了惊艳的表现。据官方消息&am…

JavaScript——前端基础3

目录 JavaScript简介 优点 可做的事情 运行 第一个JavaScript程序 搭建开发环境 安装的软件 操作 在浏览器中使用JavaScript文件 分离JS 使用node运行JS文件 语法 变量与常量 原生数据类型 模板字符串 字符串的内置方法 数组 对象 对象数组和JSON if条件语…

视频推拉流EasyDSS点播平台云端录像播放异常问题的排查与解决

EasyDSS视频直播点播平台是一个功能全面的系统&#xff0c;提供视频转码、点播、直播、视频推拉流以及H.265视频播放等一站式服务。该平台与RTMP高清摄像头配合使用&#xff0c;能够接收无人机设备的实时视频流&#xff0c;实现无人机视频推流直播和巡检等多种应用。 最近&…

简单说一下什么是RPC

部分内容来源&#xff1a;JavaGuide RPC是什么 RPC是远程调用 RPC的原理 RPC的五个部分 为了能够帮助小伙伴们理解 RPC 原理&#xff0c;我们可以将整个 RPC 的核心功能看作是下面 5 个部分实现的&#xff1a; 客户端&#xff08;服务消费端&#xff09;&#xff1a;调用…

Pany-v2:LFI漏洞探测与敏感文件(私钥窃取/其他)自动探测工具

地址:https://github.com/MartinxMax/pany 关于Pany-v2 Pany-v2 是一款 LFI&#xff08;本地文件包含&#xff09;漏洞探测工具&#xff0c;具备自动识别敏感文件的能力。它能够利用 LFI 漏洞检测并提取 id_rsa 私钥、系统密码文件以及其他可能导致安全风险的敏感信息。该工具…

北京大学DeepSeek与AIGC应用(PDF无套路下载)

近年来&#xff0c;人工智能技术飞速发展&#xff0c;尤其是大模型和生成式AI&#xff08;AIGC&#xff09;的突破&#xff0c;正在重塑各行各业的生产方式与创新路径。 北京大学联合DeepSeek团队推出的内部研讨教程《DeepSeek与AIGC应用》&#xff0c;以通俗易懂的方式系统解…

Vue进阶之Vue2源码解析

Vue2源码解析 源码解析目录解析package.json入口查找入口文件确定vue入口this.\_init_ 方法$mount 挂载方法Vue.prototype._renderVue.prototype._updateVue.prototype._patch vue2 vue3 源码解析 目录解析 vue2.6之后的版本都做的是兼容Vue3的内容&#xff0c;2.6版本前的内…

unity lua属性绑定刷新

我们现在有一个 角色属性类叫heroModel,内容如下,当heroModel中的等级发生变化的时候&#xff0c;我们需要刷新界面显示等级信息&#xff0c;通常我们是在收到等级升级成功的协议的时候&#xff0c;发送一个事件&#xff0c;UI界面接受到这个事件的时候&#xff0c;刷新一下等级…

Ubuntu20.04安装Redis

目录 切换到root用户 使用 apt install redis 安装redis 修改配置文件 ​编辑 重新启动服务器 使用Redis客户端连接服务器 切换到root用户 如果没有切换到root用户的&#xff0c;切换到root用户。 使用 apt install redis 安装redis 遇到y/n直接y即可。 redis安装好之…

OSPF BIT 类型说明

注&#xff1a;本文为 “OSPF BIT 类型 | LSA 类型 ” 相关文章合辑。 机翻&#xff0c;未校。 15 OSPF BIT Types Explained 15 种 OSPF BIT 类型说明 Rashmi Bhardwaj Distribution of routing information within a single autonomous system in larger networks is per…

【深度学习】强化学习(RL)-A3C(Asynchronous Advantage Actor-Critic)

A3C&#xff08;Asynchronous Advantage Actor-Critic&#xff09;详解 A3C&#xff08;Asynchronous Advantage Actor-Critic&#xff09; 是 深度强化学习&#xff08;Deep Reinforcement Learning, DRL&#xff09; 领域的重要算法&#xff0c;由 DeepMind 在 2016 年提出。…

在 Mac mini M2 上本地部署 DeepSeek-R1:14B:使用 Ollama 和 Chatbox 的完整指南

随着人工智能技术的飞速发展&#xff0c;本地部署大型语言模型&#xff08;LLM&#xff09;已成为许多技术爱好者的热门选择。本地部署不仅能够保护隐私&#xff0c;还能提供更灵活的使用体验。本文将详细介绍如何在 Mac mini M2&#xff08;24GB 内存&#xff09;上部署 DeepS…

docker-compose部署onlyoffice8.3.0并支持ssl,且支持通过nginx代理,关闭JWT配置

编写docker-compose文件 mkdir -p /data/onlyoffice && echo "version: 3services:onlyoffice:container_name: OnlyOfficeimage: onlyoffice/documentserver:8.3.0restart: alwaysports:- 8088:80- 64431:443environment:TZ: Asia/ShanghaiJWT_ENABLED: falsevol…

【tplink】校园网接路由器如何单独登录自己的账号,wan-lan和lan-lan区别

老式路由器TPLINK&#xff0c;接入校园网后一人登录&#xff0c;所有人都能通过连接此路由器上网&#xff0c;无法解决遂上网搜索&#xff0c;无果&#xff0c;幸而偶然看到一个帖子说要把信号源网线接入路由器lan口&#xff0c;开启新世界。 一、wan-lan&#xff0c;lan-lan区…

DeepSeek开源周Day5压轴登场:3FS与Smallpond,能否终结AI数据瓶颈之争?

2025年2月28日&#xff0c;DeepSeek开源周迎来了第五天&#xff0c;也是本次活动的收官之日。自2月24日启动以来&#xff0c;DeepSeek团队以每天一个开源项目的节奏&#xff0c;陆续向全球开发者展示了他们在人工智能基础设施领域的最新成果。今天&#xff0c;他们发布了Fire-F…

[密码学实战]Java实现SM2数字信封(结合SM4对称加密)生成与解析

一、代码运行结果 二、什么是数字信封 2.1 基本概念 数字信封(Digital Envelope) 是一种结合对称加密与非对称加密的混合加密技术,通过以下步骤实现高效安全的数据传输: 对称加密:使用SM4算法加密原始数据,处理速度快,适合大数据量。非对称加密:使用SM2公钥加密SM4密…

Oracle 查询表空间使用情况及收缩数据文件

本文介绍Oracle收缩数据文件的相关操作&#xff0c;运维工作中有时会需要通过收缩数据文件来释放磁盘空间。 数据文件初始化方式&#xff1a; 1.我们创建表空间一般有两种方式初始化其数据文件&#xff0c;即指定初始大小为32G&#xff08;很大的值&#xff09;或指定初始大小为…

Grafana接入Zabbix数据源

1. 对接 Zabbix 1.1 安装 Zabbix 插件 在线安装&#xff1a; 1.2 配置 Zabbix 数据源 点击 Configuration > Data Sources > Add data source。选择 Zabbix&#xff0c;填写&#xff1a; URL&#xff1a;http://<zabbix-server>/api_jsonrpc.phpUsername&#x…