自定義View強勢來襲,用自定義View實現歌詞顯示控件下篇之自定義LyricView的實現
在上篇中,我與大家分享了關于如何進行*.lrc歌詞文件的解析,以及將解析完成后的歌詞展示在鑲嵌在ScrollView中的TextView上,就這樣而言,一個簡單的歌詞顯示功能也就實現了。 但是,如何才能夠讓自己寫的音樂播放器在歌詞顯示模塊能夠顯得高大上,并且能夠像很多當前應用市場上流行的音樂播放器那樣,實現當前播放高亮顯示、歌詞回彈效果、歌詞淡入淡出效果以及滑動歌詞快速播放等功能呢? 請接著往下讀.....
本篇,我想要和大家分享的便是如何通過自定義View實現一款炫酷的LyricView歌詞顯示控件。看過上篇的朋友想必應該都還記得,我在上篇中有提及到在早些前我有用ScrollView嵌套TextView的方式實現過自定義LyricView,但是,由于體驗效果和功能拓展上的不足,我并沒有公開分享。既然通過ScrollView嵌套TextView的方式不能滿足我們的設計需求,那是不是能夠通過自定義View的方式實現LyricView,既有如TextView那樣的LineHeigh(行高)、LineCount(總行數)的概念,也有如ScrollView那樣的ScrollY(Y方向的偏移量)的概念。那是必須的,說干就干。
解析*.lrc歌詞文件,生成歌詞集合列表,獲得行總數
解析歌詞在本篇中我就不設篇幅進行描述了,如果還不清楚的可以翻看我的上一篇文章 《自定義View強勢來襲,用自定義View實現歌詞顯示控件上篇之實現歌詞文件解析》 。而在LyricView中,我們需要做的是將逐行解析出來的歌詞信息添加到集合 mLyricInfo 中,而總行數 mLineCount 也就等于List集合的大小 mLineCount = mLyricInfo.song_lines.size() 。
計算歌詞單行高度,獲得歌詞繪制區域總高度
寫過自定義View的朋友應該都會知道,在自定義View中如果涉及文字的繪制,為了能夠精準的繪制文字的位置,我們需要獲取需要繪制的文字的矩形區域,通過 畫筆Paint 的 getTextBounds(String text, int start, int end, Rect bounds) 方法則可以幫助我們輕松獲得一個需要繪制文字的一個矩形。當然,繪制文字矩形區域的大小還與文字的大小相關,我們還可以通過 畫筆Paint 的 setTextSize(float textSize) 方法設置繪制文字大小,也就是常說的TextSize。 當然,在LyricView中,行高可不僅僅就只是文字矩形區域的高度,行高還包括兩行之間的 行間距 ,如下圖所示。既然歌詞總行數和歌詞單行高度我們都已取得,那么獲取歌詞繪制區域的總高度也就so easy了。
計算行高度
定義scrollY,并通過當前歌曲播放進度的時間戳計算scrollY
既然LyricView能夠實現滑動功能,那么引入scrollY值記錄滑動偏移量,并控制視圖繪制效果也就順理成章。 需要明確一點,當 偏移量scrollY 的值為零的時候,歌詞的首行將顯示在整個LyricView的正中間 。在上篇中,我們也知道每一句歌詞中都包含著 開始時間 ,而我們也就可以通過當前歌曲播放進度匹配 當前播放的行數 mCurrentPlayLine ,并通過當前播放所在行,計算 偏移量scrollY 的值,控制歌詞播放滾動和當前播放位置的高亮顯示。
匹配當前播放行數 mCurrentPlayLine
計算偏移量scrollY
理論基礎已經實現,初步嘗試繪圖 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 ! 歌詞確實能夠在屏幕上繪制出來,細心的朋友也許會發現其中的幾個特別的地方,分別是當前播放位置高亮顯示和歌詞淡入淡出效果實現的代碼:
// 實現當前播放位置高亮顯示
if (i == mCurrentPlayLine - 1) {
mTextPaint.setColor(mHighLightColor);
}
// 歌詞淡入淡出效果實現
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);
}
但是,僅僅只是實現顯示功能,并且超出范圍的歌詞內容還不能通過滑動來查看。哈哈~ 別著急啊,騷年,坐下來和我涼茶,聽我細細道來。
重寫onTouchEvent,實現歌詞滑動查看,并實現overScroll回彈效果
僅僅需要實現滑動視圖的功能的話,說實話,非常簡單,只需要記錄 ACTION_DOWN 時的 y值 ,并比較 ACTION_MOVE 過程中的 y值 計算兩者的差值,生成新的 偏移量scrollY ,再刷新視圖,就可以搞定 ! 要是就這么簡簡單單了事的話,怎么也不符合我個人對完美設計的要求。要是我們無限滑動的話,整個歌詞內容區域就會滑動出我們的可視區域,也就是常說的 overScroll ,如果不加以限制將會是一種非常差的用戶體驗。當然,不同的開發對解決這個問題有不同的方法,有些播放器的歌詞顯示控件,當滑動事件出現 overScroll 時,將不再視圖繼續滑動。當然,也有當滑動事件出現 overScroll 時,視圖依舊能夠繼續滑動,但與正常滑動時有所區別,這個時候的滑動會有一種阻尼效果,也就是實際滑動距離和視圖的滾動距離并不相等,而且隨著 overScroll 的值越大,阻力越大,滑動越艱難,并在用戶手指離開屏幕后回到 overScroll 的值為零的位置。當然,我本人更喜歡后者的用戶體驗,為了實現這個功能,那么就必須要在重寫onTouchEvent的方法中"做點手腳"了。
ACTION_MOVE
阻尼大小計算
通過我一次一次對代碼的細化,只要這么簡單的兩個方法,就完成了滑動時 偏移量scrollY 的計算,包括overScroll和非overScroll,是的,只要這么兩個方法。
到了這一步,歌詞的顯示、滑動查看都已經完成。但這還沒完,我是不是還說過我的LyricView能夠實現像網易云音樂歌詞顯示控件那樣的指示器效果,哈哈哈 ~ 對于我這個完美主義者而言,這個功能必須實現。
歌詞指示器效果圖
實現歌詞指示器效果,"屌絲"蛻變"高富帥"
其實,指示器效果實現起來也不是很難,其實指示器左側的按鈕完全可以用繪制Bitmap的方式其實現,但是,考慮到LyricView的靈活性,同時,我們程序猿不都是能夠用代碼繪制的決不在工程中添加圖片的嘛 !更何況就一個簡單的播放按鈕,隨便畫畫,哈哈 ~ 至于,右側的時間指示,則是通過當前 偏移量scrollY 的值計算得來的當前控件正中間位置顯示歌詞的開始時間。
繪制指示器左側播放按鈕
繪制指示器分割線和時間
既然設計播放按鈕,當然播放按鈕就要實現點擊事件啊:
播放按鈕點擊位置判斷
播放按鈕點擊事件相應
到這一步,我們的自定義LyricView設計介紹也就告一段落咯 ! 當然,功能遠不止這些,還有設置字體大小、設置行間距、以及結合速度追蹤器實現滑行效果等等。所謂"授人以魚不如授人以漁",我想要和大家分享的是我的一個設計思路,大家可以根據需求設計不通的功能,因此在這里我也不做過多介紹,對小阿飛的LyricView感興趣的朋友可以去我的 gitHub 下載研究。
overScroll效果展示
字體顏色設置效果展示
字體大小設置效果展示
行間距設置效果展示
指示器和播放按鈕效果展示
來自:http://www.jianshu.com/p/576cbf43053e