在一些音樂類應用中, 經常會展示隨著節奏上下起伏的波紋資訊, 這些波紋形象地傳達了聲音資訊, 可以提升使用者體驗, 那麼是如何實現的呢? 可以使用Visualizer類獲取當前播放的聲音資訊, 並繪製在畫布上, 使用波紋展示即可. 我來講解一下使用方法.
主要
(1) Visualizer類提取波紋資訊的方式.
(2) 應用動態許可權管理的方法.
(3) 分離自定義檢視的展示和邏輯.
本文原始碼的GitHub下載地址
歡迎Follow我的GitHub: https://github.com/SpikeKing
基礎準備
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"/> |
效果
首頁邏輯
新增動態許可權管理, 在啟動頁面時, 獲取應用所需的音訊許可權.
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 61 62 63 64 65 66 67 68 69 70 71 72 73 |
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, 第三\四個均與回撥的返回值有關.
波紋檢視
頁面框架, 分離顯示和邏輯, 使用介面渲染, 輸入畫布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 36 37 38 39 40 41 42 43 |
/** * 音訊波紋檢視 * <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().
波紋邏輯
核心部分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 60 61 62 63 64 65 66 67 68 69 70 71 |
/** * 波紋渲染邏輯 * <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.
效果
通過繪製波紋, 可以類似地繪製一些連續資料, 更加直觀地展示, 提升使用者體驗.
That’s all! Enjoy it!
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!