android實(shí)現(xiàn)音樂跳動(dòng)效果的示例代碼
效果圖

實(shí)現(xiàn)
整體的流程圖如下

上面主要步驟分為3個(gè)
1、計(jì)算寬度能放下多少列的音頻塊。
2、計(jì)算每一列中音頻塊的個(gè)數(shù)
3、繪制音頻塊
1、計(jì)算寬度能放下多少列的音頻塊。
設(shè)置音頻塊的寬度為danceWidth,音頻塊橫向之間的間距為danceGap,那么可以算出能放的列數(shù):
/**
* 先計(jì)算當(dāng)前寬度能夠放下多少個(gè)音頻塊
*/
val widthNum = (getAvailableWith() / (danceGap + danceWidth)).toInt()
/**
* 獲取可以用的寬度
*/
private fun getAvailableWith() = mCanvasWidth - paddingLeft - paddingRight
2、計(jì)算每一列中音頻塊的個(gè)數(shù)
在算出橫向能放置多少音頻塊后,遍歷橫,然后繪制列中的音頻塊,列中的音頻塊的個(gè)數(shù)跟音頻的高低相關(guān),這里實(shí)現(xiàn)方式是通過Visualizer這個(gè)類然后獲取到mRawAudioBytes數(shù)組,
mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
@Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate) {
BaseVisualizer.this.mRawAudioBytes = bytes;
invalidate();
}
@Override
public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate) {
}
}, Visualizer.getMaxCaptureRate() / 2, true, false);
這里設(shè)置的獲取的mRawAudioBytes數(shù)組的大小是128,數(shù)組的區(qū)間范圍[-128,127],計(jì)算列的時(shí)候這里做了兩個(gè)比較重要的操作,第一個(gè)是怎么把mRawAudioBytes數(shù)組的值與音頻的個(gè)數(shù)做映射,第二個(gè)是怎么取mRawAudioBytes數(shù)組的值。
/**
* 先計(jì)算當(dāng)前寬度能夠放下多少個(gè)音頻塊
*/
val widthNum = (getAvailableWith() / (danceGap + danceWidth)).toInt()
Log.d(
TAG,
"widthNum $widthNum"
)
/**
* 算出橫向能放多少后,進(jìn)行繪制
*/
/**
* 繪制的時(shí)候用于標(biāo)記開始繪制的位置
*/
var lastDanceRight = paddingLeft.toFloat()
if (widthNum > 0 && mRawAudioBytes != null && mRawAudioBytes.isNotEmpty())
for (i in 0 until widthNum) {
//先算出當(dāng)前高度,然后再算這個(gè)高度能放下多少個(gè)音頻塊
val num = (getAvailableHeight() / (danceHeight + danceGap)).toInt()
val index = (mRawAudioBytes.size) * (i.toFloat() / widthNum)
val b = (mRawAudioBytes[index.toInt()] + 128).toFloat() / 255f
var heightNum =
(b * num).toInt()
if (heightNum < miniNum) {
heightNum = miniNum
}
if (heightNum > maxNum) {
heightNum = maxNum
}
//拿到最頂部的高度
var lastHeight = mCanvasHeight - paddingStart.toFloat()
Log.d(
TAG,
"heightNum $heightNum lastHeight $lastHeight lastDanceRight $lastDanceRight ${mRawAudioBytes[i]} $num $b $index"
)
lastHeight = drawItem(heightNum, lastDanceRight, lastHeight, canvas)
lastDanceRight += danceWidth + danceGap
}
上面做了兩個(gè)映射,首先可能有0~n橫,但是mRawAudioBytes大小是128,遍歷橫的時(shí)候?qū)ο聵?biāo)進(jìn)行一個(gè)映射,保證獲得的值是均勻的,
/** 通過這個(gè)映射得到index */ val index = (mRawAudioBytes.size) * (i.toFloat() / widthNum)
第二個(gè)映射,是得到了代表音頻大小的mRawAudioBytes數(shù)組,現(xiàn)在要把這里面的值跟列的高度做一個(gè)映射,值越大高度越高,音頻塊就越多。
val num = (getAvailableHeight() / (danceHeight + danceGap)).toInt() val b = (mRawAudioBytes[index.toInt()] + 128).toFloat() / 255f var heightNum =(b * num).toInt()
上面是先得到列最多能展示多少音頻塊,再根據(jù)mRawAudioBytes的值來算出當(dāng)前列展示多少個(gè)音頻塊。這一步也叫歸一化,區(qū)間映射。
3、繪制每一個(gè)音頻塊
private fun drawItem(
heightNum: Int,
lastDanceRight: Float,
lastHeight: Float,
canvas: Canvas?
): Float {
var lastHeight1 = lastHeight
for (j in 0 until heightNum) {
mDanceRect.set(
lastDanceRight,
lastHeight1 - danceHeight,
lastDanceRight + danceWidth,
lastHeight1
)
mPaint.shader = null
if (j >= heightNum - shaderNum) {
val backGradient = LinearGradient(
lastDanceRight,
lastHeight1 - danceHeight,
lastDanceRight + danceWidth,
lastHeight1,
intArrayOf(colorStart, colorCenter, colorEnd),
null,
Shader.TileMode.CLAMP
)
mPaint.shader = backGradient
}
canvas?.drawRoundRect(mDanceRect, 8f, 8f, mPaint)
lastHeight1 -= (danceHeight + danceGap)
}
return lastHeight1
}
就是根據(jù)高度來繪制rectangle,算出一列能繪制多少個(gè)音頻塊,每一個(gè)音頻塊是一個(gè)rectangle,然后繪制rectangle,為了效果更好,判斷上面的音頻塊加上漸變。
使用方法
<com.masoudss.lib.DanceView
android:id="@+id/danceView"
android:layout_width="320dp"
android:layout_height="300dp"
android:layout_gravity="center"
app:color_center="@color/red"
app:color_end="@color/white"
app:color_start="@color/yellow"
app:dance_color="@color/yellow"
app:dance_corner_radius="2dp"
app:dance_gap="2dp"
app:max_dance_num="30"
app:min_dance_num="2"
app:shader_num="3" />
- shader_num 頂部加漸變的個(gè)數(shù)
- color_end 漸變尾部顏色
- color_start 漸變開頭顏色
- color_center 漸變中間顏色
- min_dance_num 每一列中最少顯示的個(gè)數(shù)
- max_dance_num 每一列中最大顯示的個(gè)數(shù)
- dance_gap 每一個(gè)音頻格之間的間距
到此這篇關(guān)于android實(shí)現(xiàn)音樂跳動(dòng)效果的示例代碼的文章就介紹到這了,更多相關(guān)android 音樂跳動(dòng)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
android popwindow實(shí)現(xiàn)左側(cè)彈出菜單層及PopupWindow主要方法介紹
PopupWindow可以實(shí)現(xiàn)浮層效果,主要方法有:可以自定義view,通過LayoutInflator方法;可以出現(xiàn)和退出時(shí)顯示動(dòng)畫;可以指定顯示位置等感興趣的朋友可以了解下哦,希望本文對你學(xué)習(xí)android菜單相關(guān)開發(fā)有所幫助2013-01-01
Android實(shí)現(xiàn)在map上畫出路線的方法
這篇文章主要介紹了Android實(shí)現(xiàn)在map上畫出路線的方法,較為詳細(xì)的分析了Android在map上繪制路線所涉及的map圖調(diào)用、畫筆的使用、頁面布局及權(quán)限控制的相關(guān)技巧,需要的朋友可以參考下2015-07-07
Android 使用Vitamio打造自己的萬能播放器(6)——在線播放(播放列表)
本文主要介紹Android Vitamino在線播放列表,這里給大家提供效果圖和實(shí)例代碼以便大家參考學(xué)習(xí),希望能幫助開發(fā)Android視頻播放的朋友2016-07-07
基于SceneForm實(shí)現(xiàn)子彈射擊(繪制子彈運(yùn)行軌跡)
這篇文章主要為大家詳細(xì)介紹了基于SceneForm實(shí)現(xiàn)子彈射擊,繪制子彈運(yùn)行軌跡,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android 中API之Drawable資源詳解及簡單實(shí)例
這篇文章主要介紹了Android 中API之Drawable資源詳解及簡單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-05-05
Android開發(fā)使用strings.xml多語言翻譯解決方案
這篇文章主要為大家介紹了Android開發(fā)使用strings.xml多語言翻譯解決方案,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Android常用命令集錦(圖文并茂適應(yīng)于初學(xué)者)
大家好,今天我們要講的是android開發(fā)中,比較常用的名令集錦, 在我們開發(fā)中難免用到Android命令,有些確實(shí)命令確實(shí)很有用處,這也是我為什么總結(jié)這篇文章的原因了,希望對大家有所幫助2013-01-01

