在上篇中锨亏,我與大家分享了關(guān)于如何進行*.lrc歌詞文件的解析乾翔,以及將解析完成后的歌詞展示在鑲嵌在ScrollView中的TextView上,就這樣而言,一個簡單的歌詞顯示功能也就實現(xiàn)了。 但是,如何才能夠讓自己寫的音樂播放器在歌詞顯示模塊能夠顯得高大上谜诫,并且能夠像很多當前應用市場上流行的音樂播放器那樣划煮,實現(xiàn)當前播放高亮顯示器躏、歌詞回彈效果、歌詞淡入淡出效果以及滑動歌詞快速播放等功能呢蟹略? 請接著往下讀.....
本篇登失,我想要和大家分享的便是如何通過自定義View實現(xiàn)一款炫酷的LyricView歌詞顯示控件】坪酰看過上篇的朋友想必應該都還記得壁畸,我在上篇中有提及到在早些前我有用ScrollView嵌套TextView的方式實現(xiàn)過自定義LyricView贼急,但是茅茂,由于體驗效果和功能拓展上的不足,我并沒有公開分享太抓。既然通過ScrollView嵌套TextView的方式不能滿足我們的設(shè)計需求空闲,那是不是能夠通過自定義View的方式實現(xiàn)LyricView,既有如TextView那樣的LineHeigh(行高)走敌、LineCount(總行數(shù))的概念碴倾,也有如ScrollView那樣的ScrollY(Y方向的偏移量)的概念。那是必須的掉丽,說干就干跌榔。
解析*.lrc歌詞文件,生成歌詞集合列表捶障,獲得行總數(shù)
解析歌詞在本篇中我就不設(shè)篇幅進行描述了僧须,如果還不清楚的可以翻看我的上一篇文章《自定義View強勢來襲,用自定義View實現(xiàn)歌詞顯示控件上篇之實現(xiàn)歌詞文件解析》项炼。而在LyricView中担平,我們需要做的是將逐行解析出來的歌詞信息添加到集合mLyricInfo
中,而總行數(shù)mLineCount
也就等于List集合的大小mLineCount = mLyricInfo.song_lines.size()
锭部。
計算歌詞單行高度暂论,獲得歌詞繪制區(qū)域總高度
寫過自定義View的朋友應該都會知道,在自定義View中如果涉及文字的繪制拌禾,為了能夠精準的繪制文字的位置取胎,我們需要獲取需要繪制的文字的矩形區(qū)域,通過畫筆Paint
的getTextBounds(String text, int start, int end, Rect bounds)
方法則可以幫助我們輕松獲得一個需要繪制文字的一個矩形湃窍。當然闻蛀,繪制文字矩形區(qū)域的大小還與文字的大小相關(guān)摄杂,我們還可以通過畫筆Paint
的setTextSize(float textSize)
方法設(shè)置繪制文字大小,也就是常說的TextSize循榆。 當然析恢,在LyricView中,行高可不僅僅就只是文字矩形區(qū)域的高度秧饮,行高還包括兩行之間的行間距
映挂,如下圖所示。既然歌詞總行數(shù)和歌詞單行高度我們都已取得盗尸,那么獲取歌詞繪制區(qū)域的總高度也就so easy了柑船。
定義scrollY,并通過當前歌曲播放進度的時間戳計算scrollY
既然LyricView能夠?qū)崿F(xiàn)滑動功能泼各,那么引入scrollY值記錄滑動偏移量鞍时,并控制視圖繪制效果也就順理成章。* 需要明確一點扣蜻,當偏移量scrollY
的值為零的時候逆巍,歌詞的首行將顯示在整個LyricView的正中間 *。在上篇中莽使,我們也知道每一句歌詞中都包含著開始時間
锐极,而我們也就可以通過當前歌曲播放進度匹配當前播放的行數(shù) mCurrentPlayLine
,并通過當前播放所在行芳肌,計算偏移量scrollY
的值灵再,控制歌詞播放滾動和當前播放位置的高亮顯示。
理論基礎(chǔ)已經(jīng)實現(xiàn)亿笤,初步嘗試繪圖 onDraw
for(int i = 0, size = mLineCount; i < size; i ++) {
float x = getMeasuredWidth() * 0.5f;
float y = getMeasuredHeight() * 0.5f + (i + 0.5f) * mLineHeight - 6 - mLineSpace * 0.5f - mScrollY;
if(y + mLineHeight * 0.5f < 0) {
continue;
}
if(y - mLineHeight * 0.5f > getMeasuredHeight()) {
break;
}
if(i == mCurrentPlayLine - 1) {
mTextPaint.setColor(mHighLightColor);
} else {
if(mIndicatorShow && i == mCurrentShowLine - 1) {
mTextPaint.setColor(mCurrentShowColor);
}else {
mTextPaint.setColor(mDefaultColor);
}
}
if(y > getMeasuredHeight() - mShaderWidth || y < mShaderWidth) {
if(y < mShaderWidth) {
mTextPaint.setAlpha(26 + (int) (23000.0f * y / mShaderWidth * 0.01f));
} else {
mTextPaint.setAlpha(26 + (int) (23000.0f * (getMeasuredHeight() - y) / mShaderWidth * 0.01f));
}
} else {
mTextPaint.setAlpha(255);
}
canvas.drawText(mLyricInfo.song_lines.get(i).content, x, y, mTextPaint);
}
Bingo ! 歌詞確實能夠在屏幕上繪制出來翎迁,細心的朋友也許會發(fā)現(xiàn)其中的幾個特別的地方,分別是當前播放位置高亮顯示和歌詞淡入淡出效果實現(xiàn)的代碼:
// 實現(xiàn)當前播放位置高亮顯示
if (i == mCurrentPlayLine - 1) {
mTextPaint.setColor(mHighLightColor);
}
// 歌詞淡入淡出效果實現(xiàn)
if (y > getMeasuredHeight() - mShaderWidth || y < mShaderWidth) {
if(y < mShaderWidth) {
mTextPaint.setAlpha(26 + (int) (23000.0f * y / mShaderWidth * 0.01f));
} else {
mTextPaint.setAlpha(26 + (int) (23000.0f * (getMeasuredHeight() - y) / mShaderWidth * 0.01f));
}
} else {
mTextPaint.setAlpha(255);
}
但是净薛,僅僅只是實現(xiàn)顯示功能汪榔,并且超出范圍的歌詞內(nèi)容還不能通過滑動來查看。哈哈~ 別著急啊罕拂,騷年揍异,坐下來和我涼茶,聽我細細道來爆班。
重寫onTouchEvent衷掷,實現(xiàn)歌詞滑動查看,并實現(xiàn)overScroll回彈效果
僅僅需要實現(xiàn)滑動視圖的功能的話柿菩,說實話戚嗅,非常簡單,只需要記錄ACTION_DOWN
時的y值
,并比較ACTION_MOVE
過程中的y值
計算兩者的差值懦胞,生成新的偏移量scrollY
替久,再刷新視圖,就可以搞定 ! 要是就這么簡簡單單了事的話躏尉,怎么也不符合我個人對完美設(shè)計的要求蚯根。要是我們無限滑動的話,整個歌詞內(nèi)容區(qū)域就會滑動出我們的可視區(qū)域胀糜,也就是常說的overScroll
颅拦,如果不加以限制將會是一種非常差的用戶體驗。當然教藻,不同的開發(fā)對解決這個問題有不同的方法距帅,有些播放器的歌詞顯示控件,當滑動事件出現(xiàn)overScroll
時括堤,將不再視圖繼續(xù)滑動碌秸。當然,也有當滑動事件出現(xiàn)overScroll
時悄窃,視圖依舊能夠繼續(xù)滑動讥电,但與正常滑動時有所區(qū)別广匙,這個時候的滑動會有一種阻尼效果允趟,也就是實際滑動距離和視圖的滾動距離并不相等恼策,而且隨著overScroll
的值越大鸦致,阻力越大,滑動越艱難涣楷,并在用戶手指離開屏幕后回到overScroll
的值為零的位置分唾。當然,我本人更喜歡后者的用戶體驗狮斗,為了實現(xiàn)這個功能绽乔,那么就必須要在重寫onTouchEvent的方法中"做點手腳"了。
通過我一次一次對代碼的細化碳褒,只要這么簡單的兩個方法折砸,就完成了滑動時偏移量scrollY
的計算,包括overScroll和非overScroll沙峻,是的睦授,只要這么兩個方法。
到了這一步摔寨,歌詞的顯示去枷、滑動查看都已經(jīng)完成。但這還沒完,我是不是還說過我的LyricView能夠?qū)崿F(xiàn)像網(wǎng)易云音樂歌詞顯示控件那樣的指示器效果删顶,哈哈哈 ~ 對于我這個完美主義者而言竖螃,這個功能必須實現(xiàn)。
實現(xiàn)歌詞指示器效果逗余,"屌絲"蛻變"高富帥"
其實特咆,指示器效果實現(xiàn)起來也不是很難,其實指示器左側(cè)的按鈕完全可以用繪制Bitmap的方式其實現(xiàn)录粱,但是坚弱,考慮到LyricView的靈活性,同時关摇,我們程序猿不都是能夠用代碼繪制的決不在工程中添加圖片的嘛 荒叶!更何況就一個簡單的播放按鈕,隨便畫畫输虱,哈哈 ~ 至于些楣,右側(cè)的時間指示,則是通過當前偏移量scrollY
的值計算得來的當前控件正中間位置顯示歌詞的開始時間宪睹。
既然設(shè)計播放按鈕愁茁,當然播放按鈕就要實現(xiàn)點擊事件啊:
到這一步亭病,我們的自定義LyricView設(shè)計介紹也就告一段落咯 鹅很! 當然,功能遠不止這些罪帖,還有設(shè)置字體大小促煮、設(shè)置行間距、以及結(jié)合速度追蹤器實現(xiàn)滑行效果等等整袁。所謂"授人以魚不如授人以漁"菠齿,我想要和大家分享的是我的一個設(shè)計思路,大家可以根據(jù)需求設(shè)計不通的功能坐昙,因此在這里我也不做過多介紹绳匀,對小阿飛的LyricView感興趣的朋友可以去我的gitHub下載研究。
作者申明:如果文中有表述不當或闡述錯誤的地方炸客,還望正在看文章的您可以幫忙指出疾棵,有疑惑也可以在評論區(qū)提問或者私信,期待您的意見和建議痹仙,歡迎關(guān)注交流是尔。