android 實現(xiàn)歌詞自動滾動+手指順暢拖動

需求:
1坤塞、歌詞可以跟隨播放進度進行自動滑動冯勉;
2、可以手指進行歌詞順暢滑動摹芙;
3灼狰、當前歌詞高亮,且置于屏幕的中心浮禾;

實現(xiàn)方式一:
也是網(wǎng)上可以搜到的做多的方式:自定義view交胚,繼承textview; 通過重寫onDraw來繪制每一行text;通過onTouchEvent來控制位置盈电;但是實現(xiàn)之后發(fā)現(xiàn)手指滑動時并不順暢蝴簇。

這種方式可以參考這位作者的文章: http://www.reibang.com/p/0feb6171b0c5

本文提供另一種思路,簡單粗暴匆帚,使用listview來實現(xiàn)熬词,實現(xiàn)思路如下:

實現(xiàn)方式二、整體布局采用scrollview里面包含一個LinearLayout,LinearLayout里面依次放空View(高度為屏幕高度的一半)互拾、ListView(一行一句歌詞)歪今, 空view(高度為屏幕高度的一半)。

為什么要放兩個空view呢颜矿?是因為我們當前在聽的那句歌詞寄猩,要始終處于屏幕的中心。這個是產(chǎn)品設(shè)計骑疆。

這里面有幾個核心點:

1田篇、歌詞解析。簡單分析一下數(shù)據(jù)結(jié)構(gòu):一首歌的歌詞(Lyric)箍铭, 包含一組句子(LyricSentence), 一個句子包含一組詞或字(LyricWord)斯辰,每個LyricWord都對應(yīng)著播放時間(Duration)。我們在拖動播放進度的時候坡疼,通過拖動的百分比可以計算到要播放到的時間, 進而拿到對應(yīng)的句子和詞衣陶。我這里進度控制只控制到句子柄瑰。

2、用來放歌詞的ListView剪况,每一個item是一個TextView, 一個TextView顯示一句即可教沾。這里要注意,我們的ListView是不能有分割線译断,點擊和長按不能有其他顏色授翻,不然出來效果不好。在xml里面設(shè)置:

        <ListView
            android:id="@+id/lyric_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:divider="@null"   (設(shè)置沒有分割線)
            android:cacheColorHint="#00000000" (去除listview拖動背景色)
            android:background="@android:color/transparent" (背景透明)
            android:listSelector="@android:color/transparent" (選中狀態(tài)下背景透明孙咪,不會變黑)
            android:layout_marginRight="7dp"
            android:paddingLeft="16dp"
            android:paddingRight="7dp"/>

3堪唐、當切換到一首新的時候, 歌詞變化了翎蹈, ListView高度要跟著變化淮菠,否則顯示不全。調(diào)用以下方法重新設(shè)置listview的高度:

public static void setListViewHeightBasedOnChildren(ListView listView) {
    ListAdapter listAdapter = listView.getAdapter();
    if (listAdapter == null) {
        return;
    }
    int totalHeight = 0;
    for (int i = 0; i < listAdapter.getCount(); i++) {
        View listItem = listAdapter.getView(i, null, listView);
        listItem.measure(0, 0);
        totalHeight += listItem.getMeasuredHeight();//這個listItem.getMeasuredHeight()就是每個Item的高度

    }
    ViewGroup.LayoutParams params = listView.getLayoutParams();
    params.height = totalHeight
            + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
    listView.setLayoutParams(params);
    listView.requestLayout();
}

4荤堪、如何設(shè)置空view的高度合陵?空view高度為手機屏幕的一半, mHalfScreenHeight澄阳,我們在onSizeChanged的時候來獲取拥知。

@Override
protected void onSizeChanged(int w, int h, int ow, int oh) {
    super.onSizeChanged(w, h, ow, oh);
    if (h != 0) {
        mHalfScreenHeight = (int)(h * 0.5);
        checkBlankViewHeight();
    }
}

然后使用獲取到的mHalfScreenHeight來設(shè)置上下空view的高度。注意有時候在onSizeChanged方法中獲取之后立即去設(shè)置可能不成功碎赢,你可能需要在接收到歌詞進度變化的時候再調(diào)一下checkBlankViewHeight()低剔。

private View mTopBlankView; //上面的空view
private View mBottomBlankView; //下面的空view

private void checkBlankViewHeight() {
    if (mHalfScreenHeight != 0 && mTopBlankView.getHeight() == 0) {
        setViewHeight(mBottomBlankView, mHalfScreenHeight);
        setViewHeight(mTopBlankView, mHalfScreenHeight);
    }
}
private void setViewHeight(View view, int height) {
    ViewGroup.LayoutParams params = view.getLayoutParams();
    params.height = height;
    view.setLayoutParams(params);
}

5、切換歌曲揩抡、進度控制户侥。

private LyricAdapter mAdapter;  //這個是ListView對應(yīng)的adapter,一個item是一個textview,里面放一句歌詞LyricSentence
boolean isNewSong = false;  //是否切換到新的歌曲了

public void setLyric(Lyric lyric) {//切換歌曲镀琉,設(shè)置一首新歌的歌詞
    if (lyric == null || mAdapter.getLyric() == lyric) {
        return;
    }
    isNewSong = true;
    mAdapter.setLyric(lyric);
    setViewHeight(mListView, getListViewHeight(mListView));
}

public void setLyricCurrentPostion(int newPosition) {  // 進度控制,設(shè)置當前播放的歌曲的進度
    int lastPosition = mAdapter.getCurrentPosition();
    checkBlankViewHeight();
    // a new song is coming
    if (newPosition == 0 && isNewSong) {
        isNewSong = false;
        setDefaultPosition();
        return;
    }
    if (lastPosition == newPosition) {
        return;
    }
   // 當前進度和上次進度相比蕊唐,需要再移動多少Item的高度屋摔。每個item的高度可以參考上面的3來獲取。
    mScrollView.scrollBy(0, (newPosition - lastPosition) * mItemHeight);
    postInvalidate();
}

public void setDefaultPosition() {
    mScrollView.scrollTo(0, mAdapter.getCurrentPosition() * mItemHeight);
    postInvalidate();
 }

6替梨、最后你會發(fā)現(xiàn)可能有時候莫名其妙的位置不太對钓试,比如你明明讓他scrollto(0, 0)了但是它好像還是往上跑或者往下跑了。這是因為我們給ScrollView包含的內(nèi)容里面塞了兩個空view副瀑,當這個空view高度被我們從0設(shè)到半屏幕高的時候弓熏,ScrollView檢測到子控件的布局發(fā)生了變化,整個頁面的內(nèi)容超出了屏幕的顯示區(qū)域糠睡,所以進行了自動滾動挽鞠。我們需要寫一個不會自動滾動的ScrollView,重寫scrollview中的如下方法狈孔,并將其返回值設(shè)為0即可信认。

    @Override
     protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {  
          return 0;
     }

寫在最后:
這里只是簡單的實現(xiàn)過程的總結(jié),沒有貼大量源代碼均抽。有機會希望自己可以做一個demo出來嫁赏。

自定義View是很考驗功底的。知易行難油挥。很多坑自己不跳進去不知深淺潦蝇,每從一個坑里爬出來你就離真正的工程師更進了一步。

比如6我就debug了好久深寥,最后通過將進度條顯示出來攘乒,去觀察才發(fā)現(xiàn),原來是ScrollView自己進行了滾動惋鹅,于是就去尋找如何禁止ScrollView的解決方案持灰。但是網(wǎng)上的解決方案那么多,如何才知道哪個是可行的呢负饲?我的方案是快速試錯堤魁,通過對比分析效果,找到可行的方法返十。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妥泉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洞坑,更是在濱河造成了極大的恐慌盲链,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異刽沾,居然都是意外死亡本慕,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門侧漓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锅尘,“玉大人,你說我怎么就攤上這事布蔗√傥ィ” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵纵揍,是天一觀的道長顿乒。 經(jīng)常有香客問我,道長泽谨,這世上最難降的妖魔是什么璧榄? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮吧雹,結(jié)果婚禮上犹菱,老公的妹妹穿的比我還像新娘。我一直安慰自己吮炕,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布访得。 她就那樣靜靜地躺著龙亲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悍抑。 梳的紋絲不亂的頭發(fā)上鳄炉,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音搜骡,去河邊找鬼拂盯。 笑死,一個胖子當著我的面吹牛记靡,可吹牛的內(nèi)容都是我干的谈竿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摸吠,長吁一口氣:“原來是場噩夢啊……” “哼空凸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寸痢,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呀洲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體道逗,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡兵罢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了滓窍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卖词。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖贰您,靈堂內(nèi)的尸體忽然破棺而出坏平,到底是詐尸還是另有隱情,我是刑警寧澤锦亦,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布舶替,位于F島的核電站,受9級特大地震影響杠园,放射性物質(zhì)發(fā)生泄漏顾瞪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一抛蚁、第九天 我趴在偏房一處隱蔽的房頂上張望陈醒。 院中可真熱鬧,春花似錦瞧甩、人聲如沸钉跷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爷辙。三九已至,卻和暖如春朦促,著一層夾襖步出監(jiān)牢的瞬間膝晾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工务冕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留血当,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓禀忆,卻偏偏與公主長得像臊旭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子箩退,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,163評論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫巍扛、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,105評論 4 62
  • 以前我們一直在探討乏德,喜歡與愛到底有什么不同撤奸,爭來爭去吠昭,在數(shù)月的日子里,痛并快樂的掙扎中胧瓜,我們恍然大悟矢棚,終于可以抽象...
    6f3e65e086c5閱讀 172評論 0 1
  • 看“新東方幼兒學(xué)習(xí)部”钝满,是如何忽悠家長兜粘?(上) 【核心提示】新東方是中國最著名的民辦外語培訓(xùn)學(xué)校,其教育科技集團于...
    田園泥土香教育閱讀 981評論 3 10
  • 最近又開始好好讀書了弯蚜。 我覺得看書有一個最大的好處孔轴,就是可以釋放你無限的想象力。跟隨著作者的筆觸碎捺,你可以去天馬行空...
    圈圈o0閱讀 334評論 1 1