在一些音乐类应用中, 经常会展示随着节奏上下起伏的波纹信息, 这些波纹形象地传达了声音信息, 可以提升用户体验, 那么是如何实现的呢? 可以使用visualizer类获取当前播放的声音信息, 并绘制在画布上, 使用波纹展示即可. 我来讲解一下使用方法.
主要
(1) visualizer类提取波纹信息的方式.
(2) 应用动态权限管理的方法.
(3) 分离自定义视图的展示和逻辑.
1. 基础准备
android 6.0引入动态权限管理, 在这个项目中, 会使用系统的音频信息, 因此把权限管理引入这个项目, 参考. gradle配置引入了lambda表达式, 参考.
页面布局, 使用自定义的波纹视图控件.
1
2
3
4
5
|
<!--波纹视图--> <me.chunyu.spike.wcl_visualizer_demo.visualizers.waveformview android:id= "@+id/main_wv_waveform" android:layout_width= "match_parent" android:layout_height= "match_parent" /> |
效果
2. 首页逻辑
添加动态权限管理, 在启动页面时, 获取应用所需的音频权限.
rendererfactory工厂类创建波纹的绘制类simplewaveformrender.
startvisualiser方法获取当前播放音乐的音频信息.
注意页面关闭, 在onpause时, 释放visualiser类.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
public class mainactivity extends appcompatactivity { private static final int capture_size = 256 ; // 获取这些数据, 用于显示 private static final int request_code = 0 ; // 权限 private static final string[] permissions = new string[]{ manifest.permission.record_audio, manifest.permission.modify_audio_settings }; @bind (r.id.main_wv_waveform) waveformview mwvwaveform; // 波纹视图 private visualizer mvisualizer; // 音频可视化类 @override protected void oncreate(bundle savedinstancestate) { super .oncreate(savedinstancestate); setcontentview(r.layout.activity_main); butterknife.bind( this ); rendererfactory rendererfactory = new rendererfactory(); mwvwaveform.setrenderer(rendererfactory.createsimplewaveformrender(contextcompat.getcolor( this , r.color.colorprimary), color.white)); } @override protected void onresume() { super .onresume(); permissionschecker checker = new permissionschecker( this ); if (checker.lakespermissions(permissions)) { permissionsactivity.startactivityforresult( this , request_code, permissions); } else { startvisualiser(); } } @override protected void onactivityresult( int requestcode, int resultcode, intent data) { super .onactivityresult(requestcode, resultcode, data); if (requestcode == request_code && resultcode == permissionsactivity.permissions_denied) { finish(); } } // 设置音频线 private void startvisualiser() { mvisualizer = new visualizer( 0 ); // 初始化 mvisualizer.setdatacapturelistener( new visualizer.ondatacapturelistener() { @override public void onwaveformdatacapture(visualizer visualizer, byte [] waveform, int samplingrate) { if (mwvwaveform != null ) { mwvwaveform.setwaveform(waveform); } } @override public void onfftdatacapture(visualizer visualizer, byte [] fft, int samplingrate) { } }, visualizer.getmaxcapturerate(), true , false ); mvisualizer.setcapturesize(capture_size); mvisualizer.setenabled( true ); } // 释放 @override protected void onpause() { if (mvisualizer != null ) { mvisualizer.setenabled( false ); mvisualizer.release(); } super .onpause(); } } |
visualizer类
new visualizer(0), 初始化; setcapturesize, 获取波纹数量; setenabled, 启动监听;
setdatacapturelistener, 第一个参数是回调, 使用waveformdata或fftdata; 第二个是更新率; 第三个是判断使用waveformdata; 第四个是判断使用fftdata, 第三\四个均与回调的返回值有关.
3. 波纹视图
页面框架, 分离显示和逻辑, 使用接口渲染, 输入画布canvas和波纹waveform.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/** * 音频波纹视图 * <p> * created by wangchenlong on 16/2/11. */ public class waveformview extends view { private waveformrenderer mrenderer; // 绘制类 private byte [] mwaveform; // 波纹形状 public waveformview(context context) { super (context); } public waveformview(context context, attributeset attrs) { super (context, attrs); } public waveformview(context context, attributeset attrs, int defstyleattr) { super (context, attrs, defstyleattr); } @targetapi ( 21 ) public waveformview(context context, attributeset attrs, int defstyleattr, int defstyleres) { super (context, attrs, defstyleattr, defstyleres); } public void setrenderer(waveformrenderer renderer) { mrenderer = renderer; } public void setwaveform( byte [] waveform) { mwaveform = arrays.copyof(waveform, waveform.length); // 数组复制 invalidate(); // 设置波纹之后, 需要重绘 } @override protected void ondraw(canvas canvas) { super .ondraw(canvas); if (mrenderer != null ) { mrenderer.render(canvas, mwaveform); } } } |
数组复制arrays.copyof(), 在设置波纹后重绘页面invalidate().
4. 波纹逻辑
核心部分renderwaveform, 渲染波纹.
把页面分为网格样式, 根据波纹值, 绘制曲线; 没有波纹, 绘制居中水平直线.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
/** * 波纹渲染逻辑 * <p> * created by wangchenlong on 16/2/12. */ public class simplewaveformrenderer implements waveformrenderer { private static final int y_factor = 0xff ; // 2的8次方 = 256 private static final float half_factor = 0 .5f; @colorint private final int mbackgroundcolor; private final paint mforegroundpaint; private final path mwaveformpath; private simplewaveformrenderer( @colorint int backgroundcolor, paint foregroundpaint, path waveformpath) { mbackgroundcolor = backgroundcolor; mforegroundpaint = foregroundpaint; mwaveformpath = waveformpath; } public static simplewaveformrenderer newinstance( @colorint int backgroundcolor, @colorint int foregroundcolour) { paint paint = new paint(); paint.setcolor(foregroundcolour); paint.setantialias( true ); // 抗锯齿 paint.setstrokewidth( 8 .0f); // 设置宽度 paint.setstyle(paint.style.stroke); // 填充 path waveformpath = new path(); return new simplewaveformrenderer(backgroundcolor, paint, waveformpath); } @override public void render(canvas canvas, byte [] waveform) { canvas.drawcolor(mbackgroundcolor); float width = canvas.getwidth(); float height = canvas.getheight(); mwaveformpath.reset(); // 没有数据 if (waveform != null ) { // 绘制波形 renderwaveform(waveform, width, height); } else { // 绘制直线 renderblank(width, height); } canvas.drawpath(mwaveformpath, mforegroundpaint); } private void renderwaveform( byte [] waveform, float width, float height) { float xincrement = width / ( float ) (waveform.length); // 水平块数 float yincrement = height / y_factor; // 竖直块数 int halfheight = ( int ) (height * half_factor); // 居中位置 mwaveformpath.moveto( 0 , halfheight); for ( int i = 1 ; i < waveform.length; ++i) { float yposition = waveform[i] > 0 ? height - (yincrement * waveform[i]) : -(yincrement * waveform[i]); mwaveformpath.lineto(xincrement * i, yposition); } mwaveformpath.lineto(width, halfheight); // 最后的点, 水平居中 } // 居中画一条直线 private void renderblank( float width, float height) { int y = ( int ) (height * half_factor); mwaveformpath.moveto( 0 , y); mwaveformpath.lineto(width, y); } } |
绘制移动moveto, 绘制直线lineto.
动画效果
通过绘制波纹, 可以类似地绘制一些连续数据, 更加直观地展示, 提升用户体验.