Android使用音頻信息繪制動(dòng)態(tài)波紋
在一些音樂(lè)類(lèi)應(yīng)用中, 經(jīng)常會(huì)展示隨著節(jié)奏上下起伏的波紋信息, 這些波紋形象地傳達(dá)了聲音信息, 可以提升用戶體驗(yàn), 那么是如何實(shí)現(xiàn)的呢? 可以使用Visualizer類(lèi)獲取當(dāng)前播放的聲音信息, 并繪制在畫(huà)布上, 使用波紋展示即可. 我來(lái)講解一下使用方法.
主要
(1) Visualizer類(lèi)提取波紋信息的方式.
(2) 應(yīng)用動(dòng)態(tài)權(quán)限管理的方法.
(3) 分離自定義視圖的展示和邏輯.
1. 基礎(chǔ)準(zhǔn)備
Android 6.0引入動(dòng)態(tài)權(quán)限管理, 在這個(gè)項(xiàng)目中, 會(huì)使用系統(tǒng)的音頻信息, 因此把權(quán)限管理引入這個(gè)項(xiàng)目, 參考. Gradle配置引入了Lambda表達(dá)式, 參考.
頁(yè)面布局, 使用自定義的波紋視圖控件.
<!--波紋視圖--> <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. 首頁(yè)邏輯
添加動(dòng)態(tài)權(quán)限管理, 在啟動(dòng)頁(yè)面時(shí), 獲取應(yīng)用所需的音頻權(quán)限.
RendererFactory工廠類(lèi)創(chuàng)建波紋的繪制類(lèi)SimpleWaveformRender.
startVisualiser方法獲取當(dāng)前播放音樂(lè)的音頻信息.
注意頁(yè)面關(guān)閉, 在onPause時(shí), 釋放Visualiser類(lèi).
public class MainActivity extends AppCompatActivity { private static final int CAPTURE_SIZE = 256; // 獲取這些數(shù)據(jù), 用于顯示 private static final int REQUEST_CODE = 0; // 權(quán)限 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; // 音頻可視化類(lèi) @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(); } } // 設(shè)置音頻線 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類(lèi)
new Visualizer(0), 初始化; setCaptureSize, 獲取波紋數(shù)量; setEnabled, 啟動(dòng)監(jiān)聽(tīng);
setDataCaptureListener, 第一個(gè)參數(shù)是回調(diào), 使用WaveFormData或FftData; 第二個(gè)是更新率; 第三個(gè)是判斷使用WaveFormData; 第四個(gè)是判斷使用FftData, 第三\四個(gè)均與回調(diào)的返回值有關(guān).
3. 波紋視圖
頁(yè)面框架, 分離顯示和邏輯, 使用接口渲染, 輸入畫(huà)布Canvas和波紋Waveform.
/** * 音頻波紋視圖 * <p> * Created by wangchenlong on 16/2/11. */ public class WaveformView extends View { private WaveformRenderer mRenderer; // 繪制類(lèi) 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); // 數(shù)組復(fù)制 invalidate(); // 設(shè)置波紋之后, 需要重繪 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mRenderer != null) { mRenderer.render(canvas, mWaveform); } } }
數(shù)組復(fù)制Arrays.copyOf(), 在設(shè)置波紋后重繪頁(yè)面invalidate().
4. 波紋邏輯
核心部分renderWaveform, 渲染波紋.
把頁(yè)面分為網(wǎng)格樣式, 根據(jù)波紋值, 繪制曲線; 沒(méi)有波紋, 繪制居中水平直線.
/** * 波紋渲染邏輯 * <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); // 設(shè)置寬度 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(); // 沒(méi)有數(shù)據(jù) 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); // 水平塊數(shù) float yIncrement = height / Y_FACTOR; // 豎直塊數(shù) 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); // 最后的點(diǎn), 水平居中 } // 居中畫(huà)一條直線 private void renderBlank(float width, float height) { int y = (int) (height * HALF_FACTOR); mWaveformPath.moveTo(0, y); mWaveformPath.lineTo(width, y); } }
繪制移動(dòng)moveTo, 繪制直線lineTo.
動(dòng)畫(huà)效果
通過(guò)繪制波紋, 可以類(lèi)似地繪制一些連續(xù)數(shù)據(jù), 更加直觀地展示, 提升用戶體驗(yàn).
- Android VideoCache視頻緩存的方法詳解
- Android視頻點(diǎn)播的實(shí)現(xiàn)代碼(邊播邊緩存)
- Android音頻錄制MediaRecorder之簡(jiǎn)易的錄音軟件實(shí)現(xiàn)代碼
- Android提高之MediaPlayer播放網(wǎng)絡(luò)音頻的實(shí)現(xiàn)方法
- Android音頻可視化開(kāi)發(fā)案例說(shuō)明
- Android音頻系統(tǒng)AudioTrack使用方法詳解
- 基于VideoView自定義控制面板的視頻播放器
- 5步學(xué)會(huì)使用VideoView播放視頻
- VideoView實(shí)現(xiàn)視頻無(wú)縫連續(xù)播放
- Android視頻/音頻緩存框架AndroidVideoCache(Okhttp)詳解
相關(guān)文章
android實(shí)現(xiàn)簡(jiǎn)單拼圖游戲
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)簡(jiǎn)單拼圖游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Android微信右滑退出功能的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android微信右滑退出功能的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-01-01Android實(shí)現(xiàn)通話最小化懸浮框效果
本片內(nèi)容給大家介紹了Android音視頻通話過(guò)程中最小化成懸浮框的實(shí)現(xiàn)的方法以及代碼寫(xiě)法。2017-11-11Android應(yīng)用啟動(dòng)速度優(yōu)化
這篇文章主要介紹了Android應(yīng)用啟動(dòng)速度優(yōu)化的相關(guān)資料,需要的朋友可以參考下2016-01-01Android Service的啟動(dòng)過(guò)程分析
這篇文章主要介紹了Android Service的啟動(dòng)過(guò)程分析的相關(guān)資料,需要的朋友可以參考下2017-04-04Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解
這篇文章主要介紹了Android App中實(shí)現(xiàn)簡(jiǎn)單的刮刮卡抽獎(jiǎng)效果的實(shí)例詳解,文中主要借助Bitmap的canvas.drawPath的api來(lái)實(shí)現(xiàn),需要的朋友可以參考下2016-03-03Android四大組件:Activity/Service/Broadcast/ContentProvider作用示例
Android是一種基于Linux,自由及開(kāi)放源代碼的操作系統(tǒng),Android分為四個(gè)層,從高層到底層分別是應(yīng)用程序?qū)?、?yīng)用程序框架層、系統(tǒng)運(yùn)行庫(kù)層和Linux內(nèi)核層,Android有四大基本組件:Activity、Service服務(wù)、BroadcastReceiver廣播接收器、Content Provider內(nèi)容提供者2023-11-11Android 大文件切割與合并的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 大文件切割與合并,實(shí)現(xiàn)了很多發(fā)文件和視頻的切割,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Android Dialog對(duì)話框用法實(shí)例詳解
這篇文章主要介紹了Android Dialog對(duì)話框用法,結(jié)合實(shí)例形式分析了Android使用Dialog對(duì)話框過(guò)程中所涉及的創(chuàng)建、保存、回復(fù)等操作相關(guān)技巧與注意事項(xiàng),需要的朋友可以參考下2016-07-07