标签: 自定义view 音量波形 音波

本文目的:主要是记录自己在实现自定义view的时候,一些思路和解决方案。

目标

音量波形图

绘制两个音量波形,并且能够向右运动,上面的波形移动速度慢,下面的波形移动速度快,并且振幅能够根据音量的高低进行改变。

分解目标

先考虑静止状态,上图有两个波形图,现只考虑一个波形图,每个波形图类似于两个正弦函数的闭合。所以我们第一步要绘制一个正弦图形。

正弦函数图像

绘制正弦函数

关于自定义view的图形绘制,一般都需要onMeasure,onLayout,onDraw三个步骤。由于是自定义view,而不是viewGroup,所以并不需要实现onLayout方法。

在绘制之前,要在onMeasure方法里,计算出画布的高度、宽度、中心点等需要计算的变量,这里就不详细说明了。

为了便于绘制图形正弦函数,要把画布的坐标原点移动到绘制view的中间位置。

也就是下图中标明的点,这样坐标原点(0,0),就位于view的中间,便于函数计算。

正弦函数方法参考:

private double sine(float x, int period, float drawWidth) {

return Math.sin(2 * Math.PI * period * x / drawWidth);

}

其中period为在画布里有多少个周期,假设period为3,就是在画布里有三个周期。drawWidth为画布宽度。

在ondraw 方法里进行绘制。

这里调用drawsine方法

private void drawSine(Canvas canvas, Path path, Paint paint, int period, float drawWidth, float amplitude) {

float halfDrawWidth = drawWidth / 2f;

path.reset();

path.moveTo(-halfDrawWidth, 0);//将绘制的起点移动到最左边

float y;

for (float x = -halfDrawWidth; x <= halfDrawWidth; x++) {

y = (float) sine(x, period, drawWidth) * amplitude;

path.lineTo(x, y);

}

canvas.drawPath(path, paint);

canvas.save();

canvas.restore();

}

amplitude 为振幅的高度,也就是半个画布的高度。绘制出的图形如下(在手机里,y轴正方形是向下的,x轴正方形是向右的)

正弦函数图

绘制两个关于y轴对称正弦函数

绘制反方向正弦函数,并且填充里面的内容。只是相当于将y值乘以-1,这里不详细列出具体代码

两个正弦函数图

进行内容填充

mPaint.setStyle(Style.FILL); 画笔的样式设置为填充,填充后的效果如下

音波图

这样勉强能算作一个波形图了。

缩放波形图

观察刚开始的效果图,发现每个波形的振幅并不相同,所以要考虑对波形图进行缩放。

采用缩放函数,就是按比例将振幅逐渐增大或者减小。

double scaling;

for (float x = -halfDrawWidth; x <= halfDrawWidth; x++) {

scaling = 1 - Math.pow(x / halfDrawWidth, 2);// 对y进行缩放

y = (float) (sine(x, period, drawWidth) * amplitude * (1) * Math

.pow(scaling, 3));

path.lineTo(x, y);

}

为了更好的效果,我们缩放了三次,Math.pow(scaling, 3)

现在感觉和图一的效果差不多了。基本满足需求,就是每个波形之间的间隙还是很小。(后续会进行优化)

音波缩放图

让波形图动起来

在view里定义一个移动线程MoveThread,每隔一段时间就执行一次刷新postInvalidate(),每次刷新图像的时候,都会改变该图形的相位。

所谓相位,查看下图,一个函数是sin(x),另外一个函数是sin(x+0.5),两个函数之间就相差了0.5个相位。

正弦函数相位 + 0.5 图

相位变化了0.5,看起来就会向左移动0.5的距离。(图形右上角有标注函数)

在线程中不断更新相位的取值,这样不断的刷新图形,就会看起来形成一种移动的效果。(大家可以想象以前放电影时用的胶片,实现原理类似)这样我们的图形就能运动起来了。

修改后的sine函数

private double sine(float x, int period, float drawWidth, double phase) {

return Math.sin(2 * Math.PI * period * (x + phase) / drawWidth);

}

定义一个MoveThread

private class MoveThread extends Thread {

private static final int MOVE_STOP = 1;

private static final int MOVE_START = 0;

private int state;

@Override

public void run() {

mPhase = 0;

state = MOVE_START;

while (true) {

if (state == MOVE_STOP) {

break;

}

try {

sleep(30);

} catch (InterruptedException e) {

}

mPhase -= MOVE_DISTACE;

postInvalidate();

}

}

public void stopRunning() {

state = MOVE_STOP;

}

}

这样当线程开启的时候,我们就能根据不断的改变sine函数的相位,就会形成不断右移动的效果。

绘制两个波形,并且设置不同的移动速度

两个波形的区别只是颜色不同,最大振幅不同,以及移动速度不同。

所谓移动速度不同,就是相位每次改变的值不同。可以在计算sine函数的时候,对固定相位值乘以不同的比例,就会得到不同的移动速度。从下图中的移动我们可以看到效果,已经很接近目标了。

音波移动图形

(这里可以在图中看到不同的实现效果,为了便于有些同学学习和实践,将整个view进行了解剖,能更快的学习view的绘制过程)

根据音量改变波形图的振幅

通过音量设置波形图振幅,这样能够让波形图随着声音大小的变化而变化。

我们改变sin函数的振幅,图形就会升高或者下降。也就是在相同的x位置处,y的取值会发生变化。

振幅不同,两个正弦的高度也不同

但是,随着音频的变化,振幅的变动幅度变大,这样会造成一种图形的闪动。

解决图形闪动

当音量变化时,我们的振幅会发生变化,也就是这个图形,会随着振幅的变化按比例变大或者变小。如下图标记的两个点,如果我们刷新间隔为1s,就是1s之后,点1会突然变成点2的位置。这样就会造成闪动。

点在不同的振幅,所在的高度不同

我们的要求是图形要平滑的变动,意思就是不能这么快的进行变化,要怎么解决呢?

首先我们规定上升的最大速度为为1px每秒,现在的y值为1px,也就是当前1的位置。

现在只考虑点1的位置,假设我们每1s刷新一次,上升的最大速度为1px每秒,这样我们就可以计算出下一次变化y的最高位置为 1px + 1px/秒 * 1秒 = 2。

如果当前音量发生变化,也就是振幅发生改变,得到的y值为3px,这个时候y值,3px >

我们计算的2px,这个时候就要用我们的2px。也就保证了最大速度不能超过我们规定的速度。

如果当前音量发生变化,也就是振幅发生改变,得到的y值为1.5px,这个时候y值,1.5px <

我们计算的2px,这个时候就要用我们的1.5px。根据实际位置进行设定。

下降同理,这样我们就能保证上升或者下降的最大速度。

// 计算当前时间下的振幅

private float currentVolumeAmplitude(long curTime) {

if (lastAmplitude == nextTargetAmplitude) {

return nextTargetAmplitude;

}

if (curTime == amplitudeSetTime) {

return lastAmplitude;

}

if (nextTargetAmplitude > lastAmplitude) {

float target = lastAmplitude + mVerticalSpeed

* (curTime - amplitudeSetTime) / 1000;

if (target >= nextTargetAmplitude) {

target = nextTargetAmplitude;

lastAmplitude = nextTargetAmplitude;

amplitudeSetTime = curTime;

nextTargetAmplitude = mMinAmplitude;

}

return target;

}

if (nextTargetAmplitude < lastAmplitude) {

float target = lastAmplitude - mVerticalRestoreSpeed

* (curTime - amplitudeSetTime) / 1000;

if (target <= nextTargetAmplitude) {

target = nextTargetAmplitude;

lastAmplitude = nextTargetAmplitude;

amplitudeSetTime = curTime;

nextTargetAmplitude = mMinAmplitude;

}

return target;

}

return mMinAmplitude;

}

图形优化

因为中间的间隙过小,我们要把中间的间歇变大,类似于下图。这样效果可能会更好一点。

优化后的波形图

实施方案,将正弦函数上移,下面的正弦函数下移动,这样中间留有固定宽度的,通过缩放函数之后,效果如下:

优化后的音量波形图

实验过程中存在的问题以及解决方案:

中间线条的问题

横线的原因,是因为缩放造成了这 两个波形之间的点 x对应的值,y不等于0,会闭合不到中间的点。造成这个的现象是因为我们只是针对半个正弦曲线就进行填充了

有瑕疵的波形图

所以我们要将正反两个曲线画出来之后,把路径闭合之后再进行填充。这样就不会出现上面中间有横线的瑕疵

修复后的波形图

闪动问题

参考上文解决方案

可以直接使用的view

VolumeView.java

API: start() 开始

stop() 结束

setVolume(float volume) 设置音量

android 声波曲线动画,(自定义view实现)音量波形图相关推荐

  1. android小球爆炸动画,自定义View抛物线爆炸动画

    一.最近在写商城方面的业务,需求在商品加如购物车过程中,实现一个抛物线加入的动画. 先看我写的效果图: ezgif.com-video-to-gif (3).gif 二.编写前的设计思路是: Imag ...

  2. android 仿支付宝动画,自定义view之仿支付宝动画

    在学习本篇之前,你首先需要了解如下知识点: 自定义属性 实现效果图: 支付宝动画.gif 实现步骤: 绘制圆形 绘制对号的左边部分 绘制对号右边的部分 是不是感觉说了和没说一样,但思路就是这样 我们先 ...

  3. Android模仿淘宝语音输入条形动画,录音动画自定义View

    Android模仿淘宝语音输入条形动画自定义View,类似柱状音频,折线音频,音乐跳动,音频跳动,录音动画,语音输入效果 地址: https://github.com/xfans/VoiceWaveV ...

  4. Android实现雪花特效自定义view

    一.前言 这个冬天,老家一直没有下雨, 正好圣诞节,就想着制作一个下雪的特效. 圣诞祝福:平安夜,舞翩阡.雪花飘,飞满天.心与心,永相伴. 圣诞节是传统的宗教节日,对于基 督徒,那是庆祝耶稣的诞生,纪 ...

  5. android覆盖扩散动画,[Android]多层波纹扩散动画——自定义View绘制

    之前整理过一些属性动画的基本操作,这一段时间的动画相关需求都安然度过了.直到这次-- 一.另一种动画需求 多数交互中的动画都是让单个页面元素动起来,这种就很适合用属性动画实现.但是对于 多个元素.非页 ...

  6. android 水波纹扩散动画,[Android]多层波纹扩散动画——自定义View绘制

    之前整理过一些属性动画的基本操作,这一段时间的动画相关需求都安然度过了.直到这次-- 一.另一种动画需求 多数交互中的动画都是让单个页面元素动起来,这种就很适合用属性动画实现.但是对于 多个元素.非页 ...

  7. <android>音乐频谱显示效果 音乐播放动画 自定义view Visualizer 对接MediaPlayer 声音频率 动画效果

    最近写了一个音乐频谱显示效果的自定义view,通过Visualizer 函数对接了MediaPlayer的声源byte数据的回调,全部封装到了view的里面,外部只需要设置一个MediaPlayer即 ...

  8. android开发 鱼动画,自定义Drawable实现灵动的红鲤鱼动画(上篇)

    此篇中的小鱼动画是模仿国外一个大牛做的flash动画,第一眼就爱上它了,简约灵动又不失美学,于是抽空试着尝试了一下,如下是我用Android实现的效果图: 小鱼儿 由于整个绘制分析过程比较繁琐所以灵动 ...

  9. android炫酷的自定义view,Android自定义View实现炫酷进度条

    本文实例为大家分享了Android实现炫酷进度条的具体代码,供大家参考,具体内容如下 下面我们来实现如下效果: 第一步:创建attrs文件夹,自定义属性: 第二步:自定义View: /** * Cre ...

最新文章

  1. Python求100以内的素数和并输出
  2. 成功解决OpenVideoCall(不可用)以及MSB8020 The build tools for v141 (Platform Toolset = ‘v141‘) cannot be found
  3. Java设计模式——单件模式
  4. MySQL 笔记8 -- 存储过程和索引
  5. API(应用程序接口)是什么
  6. sci translate好用吗_收藏!SCI论文Introduction 和Conclusion的写作建议
  7. POJ 2289 Jamie's Contact Groups 【二分】+【多重匹配】(模板题)
  8. iOS13 暗黑模式(Dark Mode)适配之OC版
  9. lopatkin俄大神最新精简中文系统Windows 7 Professional VL SP1 7601.24496 x86-x64 ZH-CN DREY
  10. roller for little vGL
  11. EPSON-LQ 300K II驱动安装问题
  12. INSERT OVERWRITE LOCAL DIRECTORY
  13. CSS 滚动快照 Scroll Snap
  14. 秋招总结|阿里转正失败,到拿到10个大厂产品offer
  15. Youtube2016推荐召回算法细节及最终实现(离线服务)——完整版
  16. 创新点定义,如何写创新点
  17. Python+Selenium UI自动化 - 调用JS代码
  18. QuantLib 金融计算——收益率曲线之构建曲线(1)
  19. Win7系统下文件或程序无法选择默认打开方式如何解决
  20. 网易一元夺宝数据库分析(未涉及管理员)

热门文章

  1. OVIRT安装NVIDIA- P100实现GPU虚拟化
  2. react学习—新版Context
  3. 全面了解 Grid 布局
  4. CSS清除浮动的三种方法【前端开发】
  5. vue--富文本插件Quill(一)基础使用
  6. MITA触摸屏维修WP4053米塔工控机控制屏维修
  7. @Transactional注解和@Modifying注解
  8. 汪辟疆:《读书举要》
  9. linux 安装 lvm,Linux配置LVM
  10. AliP8大牛鼎力推荐Java岗开发进阶实战文档:Spring全家桶+Docker+Redis