自定義View強勢來襲惜颇,用自定義View實現(xiàn)歌詞顯示控件下篇之自定義LyricView的實現(xiàn)

在上篇中锨亏,我與大家分享了關(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ū)域,通過畫筆PaintgetTextBounds(String text, int start, int end, Rect bounds)方法則可以幫助我們輕松獲得一個需要繪制文字的一個矩形湃窍。當然闻蛀,繪制文字矩形區(qū)域的大小還與文字的大小相關(guān)摄杂,我們還可以通過畫筆PaintsetTextSize(float textSize)方法設(shè)置繪制文字大小,也就是常說的TextSize循榆。 當然析恢,在LyricView中,行高可不僅僅就只是文字矩形區(qū)域的高度秧饮,行高還包括兩行之間的行間距映挂,如下圖所示。既然歌詞總行數(shù)和歌詞單行高度我們都已取得盗尸,那么獲取歌詞繪制區(qū)域的總高度也就so easy了柑船。

源碼截圖 - 計算行高度
定義scrollY,并通過當前歌曲播放進度的時間戳計算scrollY

既然LyricView能夠?qū)崿F(xiàn)滑動功能泼各,那么引入scrollY值記錄滑動偏移量鞍时,并控制視圖繪制效果也就順理成章。* 需要明確一點扣蜻,當偏移量scrollY的值為零的時候逆巍,歌詞的首行將顯示在整個LyricView的正中間 *。在上篇中莽使,我們也知道每一句歌詞中都包含著開始時間锐极,而我們也就可以通過當前歌曲播放進度匹配當前播放的行數(shù) mCurrentPlayLine,并通過當前播放所在行芳肌,計算偏移量scrollY的值灵再,控制歌詞播放滾動和當前播放位置的高亮顯示。

源碼截圖 - 匹配當前播放行數(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的方法中"做點手腳"了。

源碼截圖 - ACTION_MOVE
源碼截圖 - 阻尼大小計算

通過我一次一次對代碼的細化碳褒,只要這么簡單的兩個方法折砸,就完成了滑動時偏移量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的值計算得來的當前控件正中間位置顯示歌詞的開始時間宪睹。

源碼截圖 - 繪制指示器左側(cè)播放按鈕
源碼截圖 - 繪制指示器分割線和時間

既然設(shè)計播放按鈕愁茁,當然播放按鈕就要實現(xiàn)點擊事件啊:

源碼截圖 - 播放按鈕點擊位置判斷
源碼截圖 - 播放按鈕點擊事件相應

到這一步亭病,我們的自定義LyricView設(shè)計介紹也就告一段落咯 鹅很! 當然,功能遠不止這些罪帖,還有設(shè)置字體大小促煮、設(shè)置行間距、以及結(jié)合速度追蹤器實現(xiàn)滑行效果等等整袁。所謂"授人以魚不如授人以漁"菠齿,我想要和大家分享的是我的一個設(shè)計思路,大家可以根據(jù)需求設(shè)計不通的功能坐昙,因此在這里我也不做過多介紹绳匀,對小阿飛的LyricView感興趣的朋友可以去我的gitHub下載研究。

overScroll效果展示
字體顏色設(shè)置效果展示
字體大小設(shè)置效果展示
行間距設(shè)置效果展示
指示器和播放按鈕效果展示

作者申明:如果文中有表述不當或闡述錯誤的地方炸客,還望正在看文章的您可以幫忙指出疾棵,有疑惑也可以在評論區(qū)提問或者私信,期待您的意見和建議痹仙,歡迎關(guān)注交流是尔。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蝶溶,隨后出現(xiàn)的幾起案子嗜历,更是在濱河造成了極大的恐慌宣渗,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梨州,死亡現(xiàn)場離奇詭異痕囱,居然都是意外死亡,警方通過查閱死者的電腦和手機暴匠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門鞍恢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人每窖,你說我怎么就攤上這事帮掉。” “怎么了窒典?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵蟆炊,是天一觀的道長。 經(jīng)常有香客問我瀑志,道長涩搓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任劈猪,我火速辦了婚禮昧甘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘战得。我一直安慰自己充边,他們只是感情好,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布常侦。 她就那樣靜靜地躺著浇冰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刮吧。 梳的紋絲不亂的頭發(fā)上湖饱,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音杀捻,去河邊找鬼。 笑死蚓庭,一個胖子當著我的面吹牛致讥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播器赞,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼垢袱,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了港柜?” 一聲冷哼從身側(cè)響起请契,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤咳榜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后爽锥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涌韩,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年氯夷,在試婚紗的時候發(fā)現(xiàn)自己被綠了臣樱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡腮考,死狀恐怖雇毫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情踩蔚,我是刑警寧澤棚放,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站馅闽,受9級特大地震影響席吴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捞蛋,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一孝冒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拟杉,春花似錦庄涡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拿穴,卻和暖如春泣洞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背默色。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工球凰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腿宰。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓呕诉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吃度。 傳聞我的和親對象是個殘疾皇子甩挫,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容