最近写了一个音乐频谱显示效果的自定义view,通过Visualizer 函数对接了MediaPlayer的声源byte数据的回调,全部封装到了view的里面,外部只需要设置一个MediaPlayer即可,API-> setMediaPlayer() 外层设置路径 播放之后 自动显示频谱效果,目前只做了下图一种效果,以后还会更新其它的效果。如果有赶工期或者正好碰到类似效果需求的同伴们可以直接改改来用,见下图:

然后直接上完整代码:


/*** author by LiuGuo* on 2021/4/13* 自定义组件:音乐频谱显示组件* API-> setMediaPlayer()  外层设置路径 播放之后 自动显示频谱效果*/
public class MusicFrequencyView extends View {private int widthsize;private int heightsize;private Paint paint;int count = -1;float mCount = 0;int count1 = -1;float mCount1 = 0;int count2 = -1;float mCount2 = 0;int count3 = -1;float mCount3 = 0;int count4 = -1;float mCount4 = 0;int count5 = -1;float mCount5 = 0;boolean isUp = false;boolean isUp1 = false;boolean isUp2 = false;boolean isUp3 = false;boolean isUp4 = false;boolean isUp5 = false;int[] voiceData = new int[6];private Visualizer visualizer;private int currentFrequency;private int mCurrentFrequency;private int currentVolume;private final int itemColor;private Paint paint1;private float mi;public MusicFrequencyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.musicView);itemColor = ta.getColor(R.styleable.musicView_itemColor, Color.parseColor("#000000"));}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {widthsize = MeasureSpec.getSize(widthMeasureSpec);heightsize = MeasureSpec.getSize(heightMeasureSpec);setMeasuredDimension(widthsize, heightsize);init();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.e("onDrawonDraw", "" + heightsize + "==" + widthsize);Log.e("mCountmCount", "" + count);if (count <= 0) count = 0;if (count1 <= 0) count1 = 0;if (count2 <= 0) count2 = 0;if (count3 <= 0) count3 = 0;if (count4 <= 0) count4 = 0;if (count5 <= 0) count5 = 0;mCount = heightsize / 1.04895f - count;RectF rectF = new RectF(widthsize / 5.8f, mCount, widthsize / 4.46154f, heightsize / 1.04895f);mCount1 = heightsize / 1.04895f - count1;RectF rectF1 = new RectF(widthsize / 3.41176f, mCount1, widthsize / 2.9f, heightsize / 1.04895f);mCount2 = heightsize / 1.04895f - count2;RectF rectF2 = new RectF(widthsize / 2.4166667f, mCount2, widthsize / 2.148148f, heightsize / 1.04895f);mCount3 = heightsize / 1.04895f - count3;RectF rectF3 = new RectF(widthsize / 1.870968f, mCount3, widthsize / 1.705882f, heightsize / 1.04895f);mCount4 = heightsize / 1.04895f - count4;RectF rectF4 = new RectF(widthsize / 1.526316f, mCount4, widthsize / 1.414634f, heightsize / 1.04895f);mCount5 = heightsize / 1.04895f - count5;RectF rectF5 = new RectF(widthsize / 1.28889f, mCount5, widthsize / 1.20833f, heightsize / 1.04895f);canvas.drawRect(rectF, paint);canvas.drawRect(rectF1, paint);canvas.drawRect(rectF2, paint);canvas.drawRect(rectF3, paint);canvas.drawRect(rectF4, paint);canvas.drawRect(rectF5, paint);canvas.drawLine(widthsize / 11.6f, heightsize / 1.04895f, widthsize / 1.09434f, heightsize / 1.04895f, paint);int[] ints = todoCount(count, voiceData[0], isUp);count = ints[0];isUp = ints[1] == 1 ? true : false;int[] ints1 = todoCount(count1, voiceData[1], isUp1);count1 = ints1[0];isUp1 = ints1[1] == 1 ? true : false;int[] ints2 = todoCount(count2, voiceData[2], isUp2);count2 = ints2[0];isUp2 = ints2[1] == 1 ? true : false;int[] ints3 = todoCount(count3, voiceData[3], isUp3);count3 = ints3[0];isUp3 = ints3[1] == 1 ? true : false;int[] ints4 = todoCount(count4, voiceData[4], isUp4);count4 = ints4[0];isUp4 = ints4[1] == 1 ? true : false;int[] ints5 = todoCount(count5, voiceData[5], isUp5);count5 = ints5[0];isUp5 = ints5[1] == 1 ? true : false;}/*** 判断count** @param count* @param voiceDatum* @param isUp*/private int[] todoCount(int count, int voiceDatum, boolean isUp) {if (count >= 0) {if (count < voiceDatum) {if (isUp) {count -= 17;} else {count += 50;isUp = false;}}if (count == voiceDatum) {isUp = true;count -= 17;}invalidate();}int[] ints = new int[2];ints[0] = count;ints[1] = isUp ? 1 : 0;return ints;}/*** 只需传入 mediaPlayer 即可** @param mediaPlayer*/public void setMediaPlayer(final MediaPlayer mediaPlayer) {Log.e("xiaozhu", "" + mediaPlayer.getAudioSessionId());if (visualizer != null) {visualizer = null;}visualizer = new Visualizer(mediaPlayer.getAudioSessionId());visualizer.setEnabled(false);visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);Log.e("getCaptureSizeRange", "" + Visualizer.getCaptureSizeRange()[1]);visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {@Overridepublic void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {Log.i("xiaozhu", "waveform" + bytes2HexStr(waveform));long v = 0;for (int i = 0; i < waveform.length; i++) {v += Math.pow(waveform[i], 2);}double volume = 10 * Math.log10(v / (double) waveform.length);currentVolume = (int) volume;}@Overridepublic void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {Log.i("xiaozhu", "Visualizer" + bytes2HexStr(fft));if (mediaPlayer == null || !mediaPlayer.isPlaying()) {return;}mi++;if (mi % 1 != 0) { //延缓频率
//                    return;}float[] magnitudes = new float[fft.length / 2];int max = 0;for (int i = 0; i < magnitudes.length; i++) {magnitudes[i] = (float) Math.hypot(fft[2 * i], fft[2 * i + 1]);if (magnitudes[max] < magnitudes[i]) {max = i;}}currentFrequency = max * samplingRate / fft.length;if (currentFrequency / 28000 > 28) {currentFrequency = 28;} else {currentFrequency = currentFrequency / 28000;}int[] bv = new int[6];for (int j = 0; j < 6; j++) {if (j == 0 || j == 1) {bv[j] = currentFrequency += 3;}if (j == 2 || j == 3) {bv[j] = currentFrequency -= 3;}if (j == 4 || j == 5) {bv[j] = currentFrequency -= 6;}if (currentFrequency < 3)currentFrequency = 0;bv[j] = currentFrequency;}setMusicData(bv);Log.i("xiaozhu", "currentFrequency=" + currentFrequency + "=====" + currentFrequency);}}, Visualizer.getMaxCaptureRate() / 2, true, true);visualizer.setScalingMode(Visualizer.SCALING_MODE_NORMALIZED);visualizer.setEnabled(true);mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {}});}public static String bytes2HexStr(byte[] bytes) {if (bytes == null) {return null;}StringBuilder b = new StringBuilder();for (int i = 0; i < bytes.length; i++) {b.append(String.format("%02x", bytes[i] & 0xFF));}return b.toString();}/*** 设置频率数据** @param data*/public void setMusicData(int[] data) {if (count < 0) {voiceData[0] = data[0] * 10;if (voiceData[0] > 0) {voiceData[0] = voiceData[0] + 50 - voiceData[0] % 50; //补全50的倍数 向上取整50} else {voiceData[0] = 0; //补全50的倍数 向上取整50}count = 0;isUp = false;invalidate();}if (count1 < 0) {voiceData[1] = data[1] * 10;if (voiceData[1] > 0) {voiceData[1] = voiceData[1] + 50 - voiceData[1] % 50; //补全50的倍数 向上取整50} else {voiceData[1] = 0; //补全50的倍数 向上取整50}count1 = 0;isUp1 = false;invalidate();}if (count2 < 0) {voiceData[2] = data[2] * 10;if (voiceData[2] > 0) {voiceData[2] = voiceData[2] + 50 - voiceData[2] % 50; //补全50的倍数 向上取整50} else {voiceData[2] = 0;}count2 = 0;isUp2 = false;invalidate();}if (count3 < 0) {voiceData[3] = data[3] * 10;if (voiceData[3] > 0) {voiceData[3] = voiceData[3] + 50 - voiceData[3] % 50; //补全50的倍数 向上取整50} else {voiceData[3] = 0;}count3 = 0;isUp3 = false;invalidate();}if (count4 < 0) {voiceData[4] = data[4] * 10;if (voiceData[4] > 0) {voiceData[4] = voiceData[4] + 50 - voiceData[4] % 50; //补全50的倍数 向上取整50} else {voiceData[4] = 0;}count4 = 0;isUp4 = false;invalidate();}if (count5 < 0) {voiceData[5] = data[5] * 10;if (voiceData[5] > 0) {voiceData[5] = voiceData[5] + 50 - voiceData[5] % 50; //补全50的倍数 向上取整50} else {voiceData[5] = 0;}count5 = 0;isUp5 = false;invalidate();}}/*** 初始化画笔*/void init() {setLayerType(LAYER_TYPE_SOFTWARE, null); //部分手机不显示阴影效果 配合setShadowLayerpaint = new Paint();paint.setAntiAlias(true);paint.setColor(itemColor);paint.setStyle(Paint.Style.FILL);paint.setFilterBitmap(true);paint.setShadowLayer(2, 9, 5, Color.parseColor("#55000000"));paint.setStrokeWidth(heightsize / 100f);paint1 = new Paint();paint1.setAntiAlias(true);paint1.setColor(itemColor);paint1.setStyle(Paint.Style.STROKE);paint1.setStrokeWidth(heightsize / 100f);}
}

attrs文件:

    <declare-styleable name="musicView"><attr name="itemColor" format="color"></attr></declare-styleable>

上用法:

    <com.example.xln_sideslipmenu.MusicFrequencyViewandroid:layout_width="match_parent"android:layout_height="150dp"app:itemColor="#FFD700"></com.example.xln_sideslipmenu.MusicFrequencyView>

然后创建一个MediaPlayer,通过setMediaPlayer(final MediaPlayer mediaPlayer)传入,在外部开始播放你的音乐就会自动投射到控件上该段音乐的频谱了。不过目前只做了MediaPlayer的接口,之后会慢慢对接其它播放器,另外频率显示可能会有些过于灵敏,原因是回调的频率数据分发过快,应该还有很多需要优化的地方,有做过类似功能的大神可以留言交流啊。

码云完整代码:https://gitee.com/CeMaBenTeng/three-custom-view-effects

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

  1. android 自定义view 动画效果,Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又 ...

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

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

  3. Android自定义View之仿QQ运动步数进度效果

    文章目录 前言 先看效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6e4ddec17933496ea4830fa08d8ffbe5.png?x-oss-pr ...

  4. Android绘制竖直虚线完美解决方案—自定义View

    Android绘制竖直虚线完美解决方案-自定义View 开发中我们经常会遇到绘制虚线的需求,一般我们使用一个drawable文件即可实现,下面我会先列举常规drawable文件的实现方式. 使用dra ...

  5. android 自定义view xml ,Android实现在xml文件中引用自定义View的方法分析

    本文实例讲述了Android实现在xml文件中引用自定义View的方法.分享给大家供大家参考,具体如下: 在xml中引用自定义view 方法一: android:layout_width=" ...

  6. android pcm频谱_Android  音乐频谱实现

    最近在做一款音乐播放器,设计人员新设计样图时 加了一个音乐频谱展示界面,如上图所示.这东西在Window的MediaPlayer中很常见,而且有多种效果. 但不知道怎么实现,我在文档的开发者指南里没有 ...

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

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

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

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

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

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

最新文章

  1. linux视频教程之dhcp
  2. 观咆哮有感——系统升级的疼
  3. 精选文章 什么是跨域?怎么解决跨域问题?
  4. 应用事件探查器优化SQL Server系统[转]
  5. 进阶Java架构师必看书:大型架构+框架+性能优化+中间件+分布式
  6. 二叉树的迭代统一写法
  7. Silverlight、XAML实现滚动文字
  8. TwinCAT 3 故障程序
  9. linux bridge 添加fdb,Linux协议栈--网桥设备的实现
  10. android音频系统简介
  11. MySQL事务隔离及锁机制
  12. IEEP-OSPF域内路由故障-现象与排障思路
  13. 如何测量两组汇编指令的执行效率
  14. PaddleNLP实战——LIC2021事件抽取任务基线(附代码)
  15. (区块链溯源)基于Hyperledger Fabric 区块链的产品溯源( 化妆品 )
  16. 蓝牙协议学习整理(一)蓝牙的概述
  17. 使用GoogleCode管理你的代码
  18. 02 学生成绩表(C语言)
  19. 大学生读书计划800字计算机专业,大学生读书计划范文3篇
  20. MEM 数学备考学习笔记系列:

热门文章

  1. CakePHP 中文教程3
  2. asp简介和五大内置对象
  3. 《深入理解计算机系统》(CSAPP)读书笔记 —— 第二章 信息的表示和处理
  4. 完全卸载360安全卫士方法
  5. Original error: UiAutomator2 Server cannot start because the local port #8200 is busy【因为本地端口#8200正忙】
  6. 【不需要借助第三方软件如何让电脑速度变快】
  7. Laravel5.5源码详解 -- Laravel-debugbar及使用elementUI-ajax的注意事项
  8. B 站 18 岁高中生火了:历时 200 天,成功造了个机器人!
  9. 获取数据表里的下一个自增序号
  10. window.onunload和window.onbeforeunload事件