Android實現(xiàn)音樂播放器歌詞顯示效果

這篇文章主要介紹了Android實現(xiàn)音樂播放器歌詞顯示效果,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

10多年的襄垣網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。全網(wǎng)整合營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整襄垣建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。成都創(chuàng)新互聯(lián)從事“襄垣網(wǎng)站設(shè)計”,“襄垣網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。

這兩天有個任務(wù),說是要寫一個QQ音樂播放器歌詞的那種效果,畢竟剛學自定義View,沒有什么思路,然后就Google.寫了一個歌詞效果,效果圖在后面,下面是我整理的代碼。

首先實現(xiàn)這種效果有兩種方式:

1.自定義View里重載onDraw方法,自己繪制歌詞

2.用ScrollView實現(xiàn)

第一種方式比較精確,但要支持滑動之后跳轉(zhuǎn)播放的話難度很大,所以我選擇第二種,自定義ScrollView。

我也不多說,直接上代碼,代碼中有注釋。

一.自定義LycicView extends ScrollView

里面包括一個空白布局,高度是LycicView的一半,再是一個布局存放歌詞的,最后是一個空白布局高度是LycicView的一半。

這里動態(tài)的向第二個布局里面添加了顯示歌詞的TextView,并利用ViewTreeObserver得到每個textview的高度,方便知道每個textview歌詞所要滑動到的高度。

public class LycicView extends ScrollView {
 LinearLayout rootView;//父布局
 LinearLayout lycicList;//垂直布局
 ArrayList<TextView> lyricItems = new ArrayList<TextView>();//每項的歌詞集合
 
 ArrayList<String> lyricTextList = new ArrayList<String>();//每行歌詞文本集合,建議先去看看手機音樂里的歌詞格式和內(nèi)容
 ArrayList<Long> lyricTimeList = new ArrayList<Long>();//每行歌詞所對應(yīng)的時間集合
 ArrayList<Integer> lyricItemHeights;//每行歌詞TextView所要顯示的高度
 
 int height;//控件高度
 int width;//控件寬度
 int prevSelected = 0;//前一個選擇的歌詞所在的item
 
 
 public LycicView(Context context) {
 super(context);
 init();
 }
 
 public LycicView(Context context, AttributeSet attrs) {
 super(context, attrs);
 init();
 }
 
 public LycicView(Context context, AttributeSet attrs, int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 init();
 }
 private void init(){
 rootView = new LinearLayout(getContext());
 rootView.setOrientation(LinearLayout.VERTICAL);
 //創(chuàng)建視圖樹,會在onLayout執(zhí)行后立即得到正確的高度等參數(shù)
 ViewTreeObserver vto = rootView.getViewTreeObserver();
 vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 @Override
 public void onGlobalLayout() {
 height = LycicView.this.getHeight();
 width = LycicView.this.getWidth();
 
 refreshRootView();
 
 }
 });
 addView(rootView);//把布局加進去
 }
 
 /**
 *
 */
 void refreshRootView(){
 rootView.removeAllViews();//刷新,先把之前包含的所有的view清除
 //創(chuàng)建兩個空白view
 LinearLayout blank1 = new LinearLayout(getContext());
 LinearLayout blank2 = new LinearLayout(getContext());
 //高度平分
 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width,height/2);
 rootView.addView(blank1,params);
 if(lycicList !=null){
 rootView.addView(lycicList);//加入一個歌詞顯示布局
 rootView.addView(blank2,params);
 }
 
 }
 
 /**
 *設(shè)置歌詞,
 */
 void refreshLyicList(){
 if(lycicList == null){
 lycicList = new LinearLayout(getContext());
 lycicList.setOrientation(LinearLayout.VERTICAL);
 //刷新,重新添加
 lycicList.removeAllViews();
 lyricItems.clear();
 lyricItemHeights = new ArrayList<Integer>();
 prevSelected = 0;
 //為每行歌詞創(chuàng)建一個TextView
 for(int i = 0;i<lyricTextList.size();i++){
 final TextView textView = new TextView(getContext());
 
 textView.setText(lyricTextList.get(i));
 //居中顯示
 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
 params.gravity = Gravity.CENTER_HORIZONTAL;
 textView.setLayoutParams(params);
 //對高度進行測量
 ViewTreeObserver vto = textView.getViewTreeObserver();
 final int index = i;
 vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 @Override
 public void onGlobalLayout() {
 textView.getViewTreeObserver().removeOnGlobalLayoutListener(this);//api 要在16以上 >=16
 lyricItemHeights.add(index,textView.getHeight());//將高度添加到對應(yīng)的item位置
 }
 });
 lycicList.addView(textView);
 lyricItems.add(index,textView);
 }
 }
 }
 /**
 * 滾動到index位置
 */
 public void scrollToIndex(int index){
 if(index < 0){
 scrollTo(0,0);
 }
 //計算index對應(yīng)的textview的高度
 if(index < lyricTextList.size()){
 int sum = 0;
 for(int i = 0;i<=index-1;i++){
 sum+=lyricItemHeights.get(i);
 }
 //加上index這行高度的一半
 sum+=lyricItemHeights.get(index)/2;
 scrollTo(0,sum);
 }
 }
 
 /**
 * 歌詞一直滑動,小于歌詞總長度
 * @param length
 * @return
 */
 
 int getIndex(int length){
 int index = 0;
 int sum = 0;
 while(sum <= length){
 sum+=lyricItemHeights.get(index);
 index++;
 }
 //從1開始,所以得到的是總item,腳標就得減一
 return index - 1;
 }
 
 /**
 * 設(shè)置選擇的index,選中的顏色
 * @param index
 */
 void setSelected(int index){
 //如果和之前選的一樣就不變
 if(index == prevSelected){
 return;
 }
 for(int i = 0;i<lyricItems.size();i++){
 //設(shè)置選中和沒選中的的顏色
 if(i == index){
 lyricItems.get(i).setTextColor(Color.BLUE);
 }else{
 lyricItems.get(i).setTextColor(Color.WHITE);
 }
 prevSelected = index;
 }
 }
 
 /**
 * 設(shè)置歌詞,并調(diào)用之前寫的refreshLyicList()方法設(shè)置view
 * @param textList
 * @param timeList
 */
 public void setLyricText(ArrayList<String> textList,ArrayList<Long> timeList){
 //因為你從歌詞lrc里面可以看出,每行歌詞前面都對應(yīng)有時間,所以兩者必須相等
 if(textList.size() != timeList.size()){
 throw new IllegalArgumentException();
 }
 this.lyricTextList = textList;
 this.lyricTimeList = timeList;
 
 refreshLyicList();
 }
 
 @Override
 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
 super.onScrollChanged(l, t, oldl, oldt);
 //滑動時,不往回彈,滑到哪就定位到哪
 setSelected(getIndex(t));
 if(listener != null){
 listener.onLyricScrollChange(getIndex(t),getIndex(oldt));
 }
 }
 OnLyricScrollChangeListener listener;
 public void setOnLyricScrollChangeListener(OnLyricScrollChangeListener l){
 this.listener = l;
 }
 
 /**
 * 向外部提供接口
 */
 public interface OnLyricScrollChangeListener{
 void onLyricScrollChange(int index,int oldindex);
 }
}

二..MainActivity中的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@mipmap/img01"
 tools:context=".MainActivity">
 
 <EditText
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:inputType="number"
 android:ems="10"
 android:id="@+id/editText"
 android:layout_alignParentBottom="true"
 android:layout_alignParentLeft="true"
 android:layout_alignParentStart="true" />
 
 <Button
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:text="scroll to"
 android:id="@+id/button"
 android:layout_alignTop="@+id/editText"
 android:layout_alignParentRight="true"
 android:layout_alignParentEnd="true" />
 
 <RelativeLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:layout_alignParentTop="true"
 android:layout_centerHorizontal="true"
 android:layout_above="@+id/editText">
 
 <custom.LycicView
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:id="@+id/view"
 android:layout_centerVertical="true"
 android:layout_centerHorizontal="true" />
 
 <View
 android:layout_width="match_parent"
 android:layout_height="2dp"
 android:background="@null"
 android:id="@+id/imageView"
 android:layout_centerVertical="true"
 android:layout_centerHorizontal="true" />
 <View
 android:layout_below="@id/imageView"
 android:layout_width="match_parent"
 android:layout_height="1dp"
 android:layout_marginTop="6dp"
 android:background="#999999"
 android:id="@+id/imageView2"
 android:layout_centerVertical="true"
 android:layout_centerHorizontal="true" />
 </RelativeLayout>
</RelativeLayout>

具體實現(xiàn)代碼如下:

public class MainActivity extends AppCompatActivity {
 
 LycicView view;
 EditText editText;
 Button btn;
 Handler handler = new Handler(new Handler.Callback() {
 @Override
 public boolean handleMessage(Message msg) {
 if(msg.what == 1){
 
 if(lrc_index == list.size()){
 handler.removeMessages(1);
 }
 lrc_index++;
 
 System.out.println("******"+lrc_index+"*******");
 view.scrollToIndex(lrc_index);
 handler.sendEmptyMessageDelayed(1,4000);
 }
 return false;
 }
 });
 private ArrayList<LrcMusic> lrcs;
 private ArrayList<String> list;
 private ArrayList<Long> list1;
 private int lrc_index;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 initViews();
 
 initEvents();
 }
 private void initViews(){
 view = (LycicView) findViewById(R.id.view);
 editText = (EditText) findViewById(R.id.editText);
 btn = (Button) findViewById(R.id.button);
 }
 private void initEvents(){
 InputStream is = getResources().openRawResource(R.raw.eason_tenyears);
 
 // BufferedReader br = new BufferedReader(new InputStreamReader(is));
 list = new ArrayList<String>();
 list1 = new ArrayList<>();
 lrcs = Utils.redLrc(is);
 for(int i = 0; i< lrcs.size(); i++){
 list.add(lrcs.get(i).getLrc());
 System.out.println(lrcs.get(i).getLrc()+"=====");
 list1.add(0l);//lrcs.get(i).getTime()
 }
 view.setLyricText(list, list1);
 view.postDelayed(new Runnable() {
 @Override
 public void run() {
 view.scrollToIndex(0);
 }
 },1000);
 
 btn.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 String text = editText.getText().toString();
 int index = 0;
 index = Integer.parseInt(text);
 view.scrollToIndex(index);
 }
 });
 view.setOnLyricScrollChangeListener(new LycicView.OnLyricScrollChangeListener() {
 @Override
 public void onLyricScrollChange(final int index, int oldindex) {
 editText.setText(""+index);
 lrc_index = index;
 System.out.println("===="+index+"======");
 //滾動handle不能放在這,因為,這是滾動監(jiān)聽事件,滾動到下一次,handle又會發(fā)送一次消息,出現(xiàn)意想不到的效果
 }
 });
 handler.sendEmptyMessageDelayed(1,4000);
 view.setOnTouchListener(new View.OnTouchListener() {
 @Override
 public boolean onTouch(View v, MotionEvent event) {
 switch (event.getAction()){
 case MotionEvent.ACTION_DOWN:
 handler.removeCallbacksAndMessages(null);
 
 System.out.println("取消了");
 break;
 case MotionEvent.ACTION_UP:
 System.out.println("開始了");
 handler.sendEmptyMessageDelayed(1,2000);
 break;
 case MotionEvent.ACTION_CANCEL://時間別消耗了
 break;
 }
 return false;
 }
 });
 
 getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE|WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
 }
 
}

其中utils類和LycicMusic是一個工具類和存放Music信息實體類

Utils類

public class Utils {
 public static ArrayList<LrcMusic> redLrc(InputStream in) {
 ArrayList<LrcMusic> alist = new ArrayList<LrcMusic>();
 //File f = new File(path.replace(".mp3", ".lrc"));
 try {
 //FileInputStream fs = new FileInputStream(f);
 InputStreamReader input = new InputStreamReader(in, "utf-8");
 BufferedReader br = new BufferedReader(input);
 String s = "";
 
 while ((s = br.readLine()) != null) {
 if (!TextUtils.isEmpty(s)) {
 String lyLrc = s.replace("[", "");
 String[] data_ly = lyLrc.split("]");
 if (data_ly.length > 1) {
 String time = data_ly[0];
 String lrc = data_ly[1];
 LrcMusic lrcMusic = new LrcMusic(lrcData(time), lrc);
 alist.add(lrcMusic);
 }
 }
 }
 } catch (FileNotFoundException e) {
 e.printStackTrace();
 } catch (Exception e) {
 e.printStackTrace();
 }
 return alist;
 }
 public static int lrcData(String time) {
 time = time.replace(":", "#");
 time = time.replace(".", "#");
 
 String[] mTime = time.split("#");
 
 //[03:31.42]
 int mtime = Integer.parseInt(mTime[0]);
 int stime = Integer.parseInt(mTime[1]);
 int mitime = Integer.parseInt(mTime[2]);
 
 int ctime = (mtime*60+stime)*1000+mitime*10;
 
 return ctime;
 }
}

LrcMusic實體類  

public class LrcMusic {
 private int time;
 private String lrc;
 
 public LrcMusic() {
 }
 
 public LrcMusic(int time, String lrc) {
 this.time = time;
 this.lrc = lrc;
 }
 
 public int getTime() {
 return time;
 }
 
 public void setTime(int time) {
 this.time = time;
 }
 
 public String getLrc() {
 return lrc;
 }
 
 public void setLrc(String lrc) {
 this.lrc = lrc;
 }
}

效果圖:

Android實現(xiàn)音樂播放器歌詞顯示效果

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Android實現(xiàn)音樂播放器歌詞顯示效果”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識等著你來學習!

網(wǎng)頁標題:Android實現(xiàn)音樂播放器歌詞顯示效果
文章起源:http://muchs.cn/article20/pieoco.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供云服務(wù)器營銷型網(wǎng)站建設(shè)、網(wǎng)站導航、商城網(wǎng)站全網(wǎng)營銷推廣、自適應(yīng)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)