【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.hqwc.cn/news/206179.html

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

相关文章

Redis整数集合

前言 整数集合(intset)是集合键的底层实现之一&#xff0c;当一个集合只包含整数值元素&#xff0c;并且这个集合的元素数量不多时&#xff0c;Redis就会使用整数集合作为集合键的底层实现。 一. 整数集合的实现 1.1 结构 整数集合(intset)是Redis用于保存整数值的集合抽象数据…

智能井盖传感器建设信息化时代智慧城市

近年来随着信息技术的快速发展和城市化进程的加速推进&#xff0c;智慧城市的概念逐渐成为现实。作为智慧城市生命线建设中的重要组成部分&#xff0c;智能井盖传感器的应用正在为城市的可持续发展和居民的生活质量提供新的解决方案。 智能井盖传感器能够实时监测井盖状态&…

规划类3d全景线上云展馆帮助企业轻松拓展海外市场

科技3D线上云展馆作为一种基于VR虚拟现实和互联网技术的新一代展览平台。可以在线上虚拟空间中模拟真实的展馆&#xff0c;让观众无需亲自到场&#xff0c;即可获得沉浸式的参观体验。通过这个展馆&#xff0c;您可以充分、全面、立体展示您的产品、服务以及各种创意作品&#…

解决wangeditor点击全屏被遮挡的问题

问题页面 尝试过的方法 1 添加样式 #editor-wrapper { z-index: 1000; /* 设置一个较大的值 */ } 无效 2 包裹在el-from标签里 无效 解决 把编辑器放在div中 注意在div中加上zindex&#xff0c;并且设置的层级高一点 整体代码 <div style"z-index: 1001; position…

详解Java的static关键字

文章目录 &#x1f384;静态方法&#x1f33a;静态方法和非静态方法对比&#x1f6f8;静态方法实例&#x1f6f8;非静态方法实例 &#x1f339;static关键字⭐static变量⭐static代码块 &#x1f384;静态方法 不依赖于对象实例&#xff1a;静态方法不需要依赖于任何对象实例&…

美团四年、字节三年,我的软件测试之路

前言 时间回到8年前&#xff0c;我人生中的第一份实习工作&#xff0c;是在某互联网公司做一个自动化测试工程师。当时的我可谓意气风发&#xff0c;想要大干一场&#xff0c;结果第一次做测试就出现了事故。由于对某些地方的不了解&#xff0c;把某一个地方侧漏了&#xff0c…

python练习题(markdown中的60道题)

1.Demo01 摄氏温度转化为华氏温度 celsius float(input(输入摄氏温度&#xff1a;)) fahrenheit (9/5)*celsius 32 print(%0.1f 摄氏温度转为华氏温度为 %0.1f % (celsius, fahrenheit))结果&#xff1a; 2.Demo02 计算圆柱体的体积 h, r map(float, input().split())# …

2014年8月20日 Go生态洞察:Go在OSCON的精彩亮相

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

文心大模型商业化领跑,百度在自我颠覆中重构生长力

随着科技巨头竞逐AI大模型&#xff0c;人工智能技术成为今年最受瞩目的新技术。但是&#xff0c;AI大模型的创新之路&#xff0c;还缺少一个足够有力的商业化答案。 作为全球最先发布大模型的互联网大厂&#xff0c;百度能否加速大模型的应用落地&#xff0c;以及文心大模型能…

云原生入门系列(背景和驱动力)

做任何一件事&#xff0c;或者学习、应用一个领域的技术&#xff0c;莫过于先要想好阶段的目标和理解、学习它的意义是什么&#xff1f;解决了什么问题&#xff1f; 这部分&#xff0c;就尝试来探讨下这个阶段需要理解并达成的目标以及践行云原生的意义在哪里。 1.历程 任何阶…

Windows平台Unity下实现camera场景推送RTMP|轻量级RTSP服务|实时录像

技术背景 我们在对接Unity平台camera场景采集的时候&#xff0c;除了常规的RTMP推送、录像外&#xff0c;还有一些开发者&#xff0c;需要能实现轻量级RTSP服务&#xff0c;对外提供个拉流的RTSP URL。 目前我们在Windows平台Unity下数据源可采集到以下部分&#xff1a; 采集…

计数排序+桶排序+基数排序 详讲(思路+图解+代码详解)

文章目录 计数排序桶排序基数排序一、计数排序概念&#xff1a;写法一&#xff1a;写法二&#xff1a; 二、桶排序概念代码 三、基数排序概念1.LSD排序法&#xff08;最低位优先法&#xff09;2.MSD排序法&#xff08;最高位优先法&#xff09; 基数排序VS基数排序VS桶排序 计数…