自定義View強勢來襲,用自定義View實現歌詞顯示控件下篇之自定義LyricView的實現

VernaRRDB 8年前發布 | 13K 次閱讀 安卓開發 Android開發 移動開發

在上篇中,我與大家分享了關于如何進行*.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

 

 本文由用戶 VernaRRDB 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!