【Android】声浪 UI 效果并附上详细代码

声浪效果是基于第三方实现的。
https://github.com/xfans/VoiceWaveView
将三方的 Kotlin 代码转 java 使用(按照他的readme 进行依赖,好像少了点东西,至少本项目跑不起来)

声浪效果在android 8 以上都是比较好的,不会出现断点的情况。但是在 android 8下,就会出现如下图所示的断点情况。
在这里插入图片描述

主类

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;import androidx.annotation.Nullable;import java.util.ArrayList;
import java.util.List;/*** 音浪线*/
public class VoiceWaveView extends View {private static final String TAG = "VoiceWaveView";private List<Integer> bodyWaveList = new ArrayList<>();private List<Integer> headerWaveList = new ArrayList<>();private List<Integer> footerWaveList = new ArrayList<>();private List<Integer> waveList = new ArrayList<>();private float lineSpace = 10f;private float lineWidth = 20f;private long duration = 200;private int lineColor = Color.BLUE;private Paint paintLine;private Paint paintPathLine;private ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f);private float valueAnimatorOffset = 1f;private Handler valHandler = new Handler();private Path linePath = new Path();private boolean isStart = false;private WaveMode waveMode = WaveMode.UP_DOWN;private LineType lineType = LineType.BAR_CHART;private int showGravity = Gravity.LEFT | Gravity.BOTTOM;private Runnable runnable;public VoiceWaveView(Context context) {this(context, null);}public VoiceWaveView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public VoiceWaveView(Context context, @Nullable AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(attrs);}private void init(@Nullable AttributeSet attrs) {if (attrs != null) {// Read and initialize attributes here}paintLine = new Paint();paintLine.setAntiAlias(true);paintLine.setStrokeCap(Paint.Cap.ROUND);paintPathLine = new Paint();paintPathLine.setAntiAlias(true);paintPathLine.setStyle(Paint.Style.STROKE);valueAnimator.addUpdateListener(animation -> {valueAnimatorOffset = (float) animation.getAnimatedValue();invalidate();});}public void setLineSpace(float lineSpace) {this.lineSpace = lineSpace;}public void setLineWidth(float lineWidth) {this.lineWidth = lineWidth;}public void setDuration(long duration) {this.duration = duration;}public void setLineColor(int lineColor) {this.lineColor = lineColor;}public void setWaveMode(WaveMode waveMode) {this.waveMode = waveMode;}public void setLineType(LineType lineType) {this.lineType = lineType;}public void setShowGravity(int showGravity) {this.showGravity = showGravity;}public VoiceWaveView addBody(int soundLevel) {checkNum(soundLevel);bodyWaveList.add(soundLevel);return this;}public VoiceWaveView initBody(int length, int soundLevel) {bodyWaveList.clear();for (int i = 0; i < length; i++) {addBody(soundLevel);}return this;}// TODO: 2023/11/1 中间弹的的逻辑public VoiceWaveView refreshBody(int soundLevel) {// 添加 soundLevel 到头部bodyWaveList.add(0, soundLevel);// 递减相邻元素的值for (int i = 1; i < bodyWaveList.size() - 1; i++) {int previousValue = bodyWaveList.get(i - 1);int currentValue = bodyWaveList.get(i);int nextValue = bodyWaveList.get(i + 1);int updatedValue = Math.max(currentValue - 1, Math.max(previousValue, nextValue) - 2);bodyWaveList.set(i, updatedValue);}return this;}/*** 刷新最后一个** @param soundLevel*/public void updateBody(int soundLevel) {bodyWaveList.remove(bodyWaveList.size() - 1);addBody(soundLevel);}public VoiceWaveView addHeader(int soundLevel) {checkNum(soundLevel);headerWaveList.add(soundLevel);return this;}public VoiceWaveView addFooter(int soundLevel) {checkNum(soundLevel);footerWaveList.add(soundLevel);return this;}private void checkNum(int soundLevel) {if (soundLevel < 0 || soundLevel > 100) {throw new IllegalArgumentException("num must be between 0 and 100");}}public void start() {if (isStart) {return;}L.i(TAG, "start ");isStart = true;if (waveMode == WaveMode.UP_DOWN) {valueAnimator.setDuration(duration);valueAnimator.setRepeatMode(ValueAnimator.REVERSE);valueAnimator.setRepeatCount(ValueAnimator.INFINITE);valueAnimator.start();} else if (waveMode == WaveMode.LEFT_RIGHT) {runnable = new Runnable() {@Overridepublic void run() {//日志类,自己构建即可L.i(TAG, bodyWaveList.toString());Integer last = bodyWaveList.remove(bodyWaveList.size() - 1);bodyWaveList.add(0, last);invalidate();valHandler.postDelayed(this, duration);}};valHandler.post(runnable);}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);L.i(TAG, "onDraw ");waveList.clear();waveList.addAll(headerWaveList);waveList.addAll(bodyWaveList);waveList.addAll(footerWaveList);linePath.reset();paintPathLine.setStrokeWidth(lineWidth);paintPathLine.setColor(lineColor);paintLine.setStrokeWidth(lineWidth);paintLine.setColor(lineColor);float measuredWidth = getMeasuredWidth();float measuredHeight = getMeasuredHeight();float startX = 0f;float startY = 0f;float endX = 0f;float endY = 0f;for (int i = 0; i < waveList.size(); i++) {float offset = 1f;if (i >= headerWaveList.size() && i < (waveList.size() - footerWaveList.size())) {offset = valueAnimatorOffset;}float lineHeight = (waveList.get(i) / 100.0f) * measuredHeight * offset;int absoluteGravity = Gravity.getAbsoluteGravity(showGravity, getLayoutDirection());switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {case Gravity.CENTER_HORIZONTAL:int lineSize = waveList.size();float allLineWidth = lineSize * (lineSpace + lineWidth);if (allLineWidth < measuredWidth) {startX = (i * (lineSpace + lineWidth) + lineWidth / 2) + ((measuredWidth - allLineWidth) / 2);} else {startX = i * (lineSpace + lineWidth) + lineWidth / 2;}endX = startX;break;case Gravity.RIGHT:lineSize = waveList.size();allLineWidth = lineSize * (lineSpace + lineWidth);if (allLineWidth < measuredWidth) {startX = (i * (lineSpace + lineWidth) + lineWidth / 2) + (measuredWidth - allLineWidth);} else {startX = i * (lineSpace + lineWidth) + lineWidth / 2;}endX = startX;break;case Gravity.LEFT:startX = i * (lineSpace + lineWidth) + lineWidth / 2;endX = startX;break;}switch (showGravity & Gravity.VERTICAL_GRAVITY_MASK) {case Gravity.TOP:startY = 0f;endY = lineHeight;break;case Gravity.CENTER_VERTICAL:startY = (measuredHeight / 2 - lineHeight / 2);endY = (measuredHeight / 2 + lineHeight / 2);break;case Gravity.BOTTOM:startY = (measuredHeight - lineHeight);endY = measuredHeight;break;}if (lineType == LineType.BAR_CHART) {canvas.drawLine(startX, startY, endX, endY, paintLine);}if (lineType == LineType.LINE_GRAPH) {if (i == 0) {linePath.moveTo(startX, startY);float pathEndX = endX + (lineWidth / 2) + (lineSpace / 2);linePath.lineTo(pathEndX, endY);} else {linePath.lineTo(startX, startY);float pathEndX = endX + (lineWidth / 2) + (lineSpace / 2);linePath.lineTo(pathEndX, endY);}}}if (lineType == LineType.LINE_GRAPH) {canvas.drawPath(linePath, paintPathLine);}}public void stop() {L.i(TAG, "stop ");isStart = false;if (runnable != null) {valHandler.removeCallbacks(runnable);}valueAnimator.cancel();}@Overrideprotected Parcelable onSaveInstanceState() {// TODO onSaveInstanceStatereturn super.onSaveInstanceState();}@Overrideprotected void onRestoreInstanceState(Parcelable state) {// TODO onRestoreInstanceStatesuper.onRestoreInstanceState(state);}
}

相关枚举类

public enum LineType {LINE_GRAPH(0),BAR_CHART(1);private int value;private LineType(int value) {this.value = value;}public int value() {return this.value;}
}
public enum WaveMode {UP_DOWN,LEFT_RIGHT
}

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

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

相关文章

外卖配送小程序商城的效果如何

线下餐饮店非常多&#xff0c;主要以同城生意为主&#xff0c;在线上电商和外卖平台的冲击下&#xff0c;传统商家仅通过传统方式经营很难宣传拓客及转化等&#xff0c;线上是必要的渠道&#xff0c;但入驻第三方平台又会有各种困扰&#xff0c;抽成/佣金/流量费/激烈竞争等。 …

【云原生-Kurbernetes篇】 玩转K8S不得不会的HELM

Helm 一、Helm1.1 使用背景1.2 Helm简介1.3 Helm的几个概念1.4 helm2 和 helm3 的区别1.5 chart包的关键组成 二、Helm相关命令2.1 应用管理操作2.2 Helm repository仓库管理命令2.2 Helm chart包管理命令2.3 Helm release(实例) 管理命令2.4 Helm私有仓库管理命令 三、部署He…

Linux常用操作 Vim一般使用 SSH介绍 SSH密钥登录

目录 1. 常用命令 2. vim一般使用 3. SSH介绍 4. ssh密钥登录 1. 常用命令 1&#xff09;# 与 $ 提示的区别 # 表示用户有root权限&#xff0c;一般的以root用户登录提示符为#&#xff0c; $提示符表示用户为普通用户 2&#xff09;ifconfig 查看ip地址 eno1: 代表由主板…

“图纸保密大作战:上海迅软DSE解决方案守护机械公司核心资料

机械行业是我国重要的工业制造行业之一&#xff0c;相关企业在发展中往往需要用到ERP、PDM、PLM等系统来对产品信息进行管理&#xff0c;其中便涉及到大量文档和图纸等重要数据。然而随着业务的快速发展和数字化转型&#xff0c;机械行业也面临着如数据泄露、外来袭击攻击、内部…

以45°斜抛水平距离最远

已知&#xff1a;斜抛物体的初速度为 v 0 v_0 v0​&#xff08;与水平方向的夹角为 θ \theta θ&#xff09;&#xff0c;重力加速度为 g g g。 求&#xff1a;抛物轨迹方程&#xff1f; 垂直方向的速度为 v y v 0 sin ⁡ θ − g t v_yv_0 \sin \theta -gt vy​v0​sinθ−…

CNVD-2023-12632:泛微E-cology9 browserjsp SQL注入漏洞复现 [附POC]

文章目录 泛微E-cology9 browserjsp SQL注入漏洞(CNVD-2023-12632)漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 泛微E-cology9 browserjsp SQL注入漏洞(CNVD-2023-12632)漏洞复现 [附POC] 0x…

如何查找批量企业的联系方式?

​我们都知道&#xff0c;企业的联系方式在企业的年报中就能找到&#xff0c;但是年报上的电话真的是你要找的吗&#xff1f; 很多企业年报上留的是第三方代记账公司&#xff0c;或者是其他没用的号码&#xff0c;这对于做B端业务的企业来说是不够精准的。 市面上有很多做企业…

一起学docker系列之六如何搭建私服版本的Docker镜像仓库

目录 前言1 下载并运行私服版本的Docker镜像仓库2 准备上传私服的Docker镜像3 为镜像打上符合私服规范的标签4 修改Docker守护进程的配置文件5 推送镜像到私服版本的Docker镜像仓库6 验证私服的镜像结语 前言 Docker是一种开源的容器技术&#xff0c;可以让开发者和运维人员快…

Android : ListView + BaseAdapter-2简单应用

​​容器与适配器&#xff1a;​​​​​ http://t.csdnimg.cn/ZfAJ7 实体类 News.java package com.example.mylistviewadapter2.entity;public class News {private String title;private String content;private int img;public News(String title, String conte…

金蝶云星空部署包导出文件

文章目录 金蝶云星空部署包导出文件 金蝶云星空部署包导出文件 打开补丁包后&#xff0c;贴入导出文件的文件夹&#xff0c;然后按F2即可导出到目标文件夹。

2023年最佳Aspera替代方案,选择适合的Aspera替代方案

查找当前可用的Aspera替代方案。比较 2023年Aspera替代方案的评级、评论、定价和功能。列出了市场上最好的Aspera替代方案&#xff0c;它们提供与 IBM Aspera 类似的竞争产品。对下面的Aspera替代方案进行排序&#xff0c;以根据需求做出最佳选择。 1、镭速 镭速&#xff08;私…

java继承和重写(代码演示)

java中的继承和重写 概念 继承 在 Java 中&#xff0c;继承是面向对象编程中的重要概念&#xff0c;它允许一个类&#xff08;称为子类&#xff09;继承另一个类&#xff08;称为父类&#xff09;的属性和方法。子类可以继承父类的非私有属性和方法&#xff0c;并且可以添加…

每日一题 2304. 网格中的最小路径代价(中等,动态规划)

由于他每一行的每一个值都可以到下一行的所有节点&#xff0c;且路径的代价没有什么相关性&#xff0c;所以只能用 O(mn2) 的动态规划求解 class Solution:def minPathCost(self, grid: List[List[int]], moveCost: List[List[int]]) -> int:m, n len(grid), len(grid[0])…

PC分页操作

page-size 每页显示条目个数 current-page 当前页数 total 数据总数 current-change【currentPage 改变时会触发】 <el-paginationbackgroundlayout"prev, pager, next"align"right"style"padding: 10px":page-size"pageParams.pagesize…

连接k8s和凌鲨

通过连接k8s和凌鲨&#xff0c;可以让研发过程中的重用操作更加方便。 更新容器镜像调整部署规模查看日志运行命令 架构 所有操作通过k8s proxy连接&#xff0c;通过设置namespace label赋予访问权限。只有赋予特定label的namespace才能被访问。 使用步骤 部署k8s proxy 你…

【Java】基于SaaS模式的Java基层医院卫生健康云HIS系统源码

一、模板管理 模板分为两种&#xff1a;病历模板和报表模板。模板管理是运营管理的核心组成部分&#xff0c;是基层卫生健康云中各医疗机构定制电子病历和报表的地方&#xff0c;各医疗机构可根据自身特点特色定制电子病历和报表&#xff0c;制作的电子病历及报表可直接在业务…

【qsort学习及改造冒泡排序能排序任何数】

qsort学习及改造冒泡排序能排序任何数 qsort的使用 qsort的使用 这个函数也不是很复杂&#xff01;&#xff01;&#xff01; qsort(void*base,size_t num,size_t width,int(int (__cdecl *compare )(const void *elem1, const void *elem2 )))  void * base,为数组的基地…

【深度学习】python调用超分Real-ESRGAN

Real-ESRGAN是超分自然场景图和动漫图&#xff0c;视频也可以&#xff0c;项目地址&#xff1a;https://github.com/xinntao/Real-ESRGAN/tree/master 安装python包&#xff1a; basicsr>1.4.2 facexlib>0.2.5 gfpgan>1.3.5 numpy opencv-python Pillow torch>1.…

GD32替换STM32使用HAL库开发问题

GD32HAL库开发问题 1can初始化进入error handle2发送邮箱不能按照填写顺序发送3 GD32修改代码被stm32cudemx覆盖问题 1can初始化进入error handle HAL库的HAL_CAN_Init中&#xff0c;hcan->Instance->MSR寄存器无法清零&#xff0c;STM32先清零&#xff0c;再退出睡眠模…

编译 CUDA加速的 OpenCV-4.8.0 版本

文章目录 前言一、编译环境二、前期准备三、CMake编译四、VS编译OpenCV.sln五、问题 前言 由于项目需要用上CUDA加速的OpenCV&#xff0c;编译时也踩了不少坑&#xff0c;所以这里记录一下。 一、编译环境 我的编译环境是&#xff1a; Win10 RTX4050 CUDA-12.0 CUDNN 8.9.…