Android使用音频信息绘制动态波纹

在一些音乐类应用中, 经常会展示随着节奏上下起伏的波纹信息, 这些波纹形象地传达了声音信息, 可以提升用户体验, 那么是如何实现的呢? 可以使用Visualizer类获取当前播放的声音信息, 并绘制在画布上, 使用波纹展示即可. 我来讲解一下使用方法.

主要

(1) Visualizer类提取波纹信息的方式.
(2) 应用动态权限管理的方法.
(3) 分离自定义视图的展示和逻辑.

1. 基础准备

Android 6.0引入动态权限管理, 在这个项目中, 会使用系统的音频信息, 因此把权限管理引入这个项目, 参考. Gradle配置引入了Lambda表达式, 参考.

页面布局, 使用自定义的波纹视图控件.

<!--波纹视图-->
<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类.

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.

/**
* 音频波纹视图
* <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, 渲染波纹.
把页面分为网格样式, 根据波纹值, 绘制曲线; 没有波纹, 绘制居中水平直线.

/**
* 波纹渲染逻辑
* <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.

动画效果

通过绘制波纹, 可以类似地绘制一些连续数据, 更加直观地展示, 提升用户体验.

(0)

相关推荐

  • Android开发 OpenGL ES绘制3D 图形实例详解

    OpenGL ES是 OpenGL三维图形API 的子集,针对手机.PDA和游戏主机等嵌入式设备而设计. Ophone目前支持OpenGL ES 1.0 ,OpenGL ES 1.0 是以 OpenGL 1.3 规范为基础的,OpenGL ES 1.1 是以 OpenGL 1.5 规范为基础的.本文主要介绍利用OpenGL ES绘制图形方面的基本步骤. 本文内容由三部分构成.首先通过EGL获得OpenGL ES的编程接口;其次介绍构建3D程序的基本概念;最后是一个应用程序示例. OpenGL E

  • Android开发之OpenGL绘制2D图形的方法分析

    本文实例讲述了Android开发之OpenGL绘制2D图形的方法.分享给大家供大家参考,具体如下: Android为OpenGL ES支持提供了GLSurviceView组建,这个组建用于显示3D图形.GLSurviceView本身并不提供绘制3的图形的功能,而是由GLSurfaceView.Renderer来完成了SurviceView中3D图形的绘制. 归纳起来,在android中使用OpenGL ES需要3个步骤. 1. 创建GLSurviceView组件,使用Activity来显示GLS

  • Android自定义View之继承TextView绘制背景

    本文实例为大家分享了TextView绘制背景的方法,供大家参考,具体内容如下 效果: 实现流程: 1.初始化:对画笔进行设置 mPaintIn = new Paint(); mPaintIn.setAntiAlias(true); mPaintIn.setDither(true); mPaintIn.setStyle(Paint.Style.FILL); mPaintIn.setColor(getResources().getColor(R.color.colorPrimary)); mPain

  • Android编程实现扭曲图像的绘制功能示例

    本文实例讲述了Android编程实现扭曲图像的绘制功能.分享给大家供大家参考,具体如下: 为了实现动画效果,使用drawBitmapMess方法对图像进行扭曲,使用定时器以100毫秒的频率按圆形轨迹扭曲图像. 扭曲的关键是生成verts数组.本例一开始会先生成verts数组的初始值:有一定水平和垂直间距的网点坐标.然后通过warp方法按一定的数学方法变化verts数组中的坐标.关键部分的代码如下: 定义基本变量:MyView是用于显示扭曲的图像的自定义view,angle是圆形轨迹的当前角度:

  • Android编程绘制圆形图片的方法

    本文实例讲述了Android编程绘制圆形图片的方法.分享给大家供大家参考,具体如下: 效果图如下: 第一步:新建RoundView自定义控件继承View package com.rong.activity; import com.rong.test.R; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.grap

  • Android shape 绘制图形的实例详解

    Android shape 绘制图形 Android 绘制图形可以使用shape也可以使用自定义控件的方式,这里我们说下shape的方式去实现. 在绘制图形之前,我们先来了解下shape的几个属性. shape /* * 线行 圆形 矩形 / android:shape="line" android:shape="oval" android:shape="rectangle" size 图形的大小 <size android:height=

  • Android编程之canvas绘制各种图形(点,直线,弧,圆,椭圆,文字,矩形,多边形,曲线,圆角矩形)

    本文实例讲述了Android编程之canvas绘制各种图形的方法.分享给大家供大家参考,具体如下: 1.首先说一下canvas类: Class Overview The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into

  • Android绘制圆形百分比加载圈效果

    先看一组加载效果图,有点粉粉的加载圈: 自定义这样的圆形加载圈还是比较简单的,主要是用到Canvans的绘制文本,绘制圆和绘制圆弧的api: /** * 绘制圆 * @param cx 圆心x坐标 * @param cy 圆心y坐标 * @param radius 圆的半径 * @param paint 画笔 */ public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) { ... } /**

  • Android编程绘图操作之弧形绘制方法示例

    本文实例讲述了Android编程绘图操作之弧形绘制方法.分享给大家供大家参考,具体如下: /** * 绘制弧形图案 * @description: * @author ldm * @date 2016-4-25 下午4:37:01 */ public class ArcsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedIns

  • Android开发之绘制平面上的多边形功能分析

    本文实例讲述了Android开发之绘制平面上的多边形功能.分享给大家供大家参考,具体如下: 计算机里的3D图形其实是由很多个平面组合而成的.所谓"绘制3D"图形,其实是通过多个平面图形形成的.调用GL10图形绘制2D图形的步骤如下: i. 调用GL10的glEnableClientState(GL10.GL_VERTEX_ARRAY);方法启用顶点坐标数组. ii. 调用GL10的glEnableClientState(GL10.GL_COLOR_ARRAY);方法启用顶点颜色数组.

  • Android中使用ListView绘制自定义表格技巧分享

    先上一下可以实现的效果图  要实现的效果有几方面 1.列不固定:可以根据数据源的不同生成不同的列数 2.表格内容可以根据数据源的定义合并列 3.要填写的单元格可以选择自定义键盘还是系统键盘 奔着这三点,做了个简单的实现,把源码贴一下(因为该点是主界面中的一部分,不便于放整个Demo) 自定义适配器,CallBackInterface是自定义的回调接口,这里定义回调是因为数据输入时需要及时保存 复制代码 代码如下: public class SiteDetailViewAdapter extend

  • 解决Android SurfaceView绘制触摸轨迹闪烁问题的方法

    本文分享了解决SurfaceView触摸轨迹闪烁问题的方法,供大家参考,具体内容如下 第一种解决SurfaceView触摸轨迹闪烁问题的方法: 由于SurfaceView使用双缓存机制,两张画布轮流显示到屏幕上.那么,要存储触摸轨迹并避免两张画布内容不一致造成的闪烁问题,完全可以利用保存绘制过程并不断重新绘制的方法解决闪烁,而且这样还顺带解决了多次试验中偶尔出现的因为moveTo()函数不能读取到参数执行默认设置(参数设为上次的触摸点)而出现的断线连接闪烁问题,详细代码如下: package c

随机推荐