Android实现音乐播放器歌词显示效果

发布时间:2019-08-08 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Android实现音乐播放器歌词显示效果脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

这两天有个任务,说是要写一个QQ音乐播放器歌词的那种效果,毕竟刚学自定义View,没有什么思路,然后就GOOGLE.写了一个歌词效果,效果图在后面,下面是我整理的代码。

首先实现这种效果有两种方式:

1.自定义View里重载onDraw方法,自己绘制歌词

2.用ScrollView实现

第一种方式比较精确,但要支持滑动之后跳转播放的话难度很大,所以我选择第二种,自定义ScrollView。

我也不多说,直接上代码,代码中有注释。

一.自定义LycicView extends ScrollView

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

这里动态的向第二个布局里面添加了显示歌词的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>();//每行歌词文本集合,建议先去看看手机音乐里的歌词格式和内容  ArrayList<Long> lyricTimeList = new ArrayList<Long>();//每行歌词所对应的时间集合  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);  //创建视图树,会在onLayout执行后立即得到正确的高度等参数  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清除  //创建两个空白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);  }    }    /**  *设置歌词,  */  void refreshLyicList(){  if(lycicList == null){  lycicList = new LinearLayout(getContext());  lycicList.setOrientation(LinearLayout.VERTICAL);  //刷新,重新添加  lycicList.removeAllViews();  lyricItems.clear();  lyricItemHeights = new ArrayList<Integer>();  prevSelected = 0;  //为每行歌词创建一个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());//将高度添加到对应的item位置  }  });  lycicList.addView(textView);  lyricItems.add(index,textView);  }  }  }  /**  * 滚动到index位置  */  public void scrollToIndex(int index){  if(index < 0){  scrollTo(0,0);  }  //计算index对应的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;  }    /**  * 设置选择的index,选中的颜色  * @param index  */  void setSelected(int index){  //如果和之前选的一样就不变  if(index == prevSelected){  return;  }  for(int i = 0;i<lyricItems.size();i++){  //设置选中和没选中的的颜色  if(i == index){  lyricItems.get(i).setTextColor(Color.BLUE);  }else{  lyricItems.get(i).setTextColor(Color.WHITE);  }  prevSelected = index;  }  }    /**  * 设置歌词,并调用之前写的refreshLyicList()方法设置view  * @param textList  * @param timeList  */  public void setLyricText(ArrayList<String> textList,ArrayList<Long> timeList){  //因为你从歌词lrc里面可以看出,每行歌词前面都对应有时间,所以两者必须相等  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中的布局

 <&#63;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>

具体实现代码如下:

 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不能放在这,因为,这是滚动监听事件,滚动到下一次,handle又会发送一次消息,出现意想不到的效果  }  });  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实体类&nbsp; 

 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实现音乐播放器歌词显示效果

大体就这样,如有无情纠正,附上码地址:点击打开链接

android教程@H_777_63@ 脚本网站
android studio

脚本宝典总结

以上是脚本宝典为你收集整理的Android实现音乐播放器歌词显示效果全部内容,希望文章能够帮你解决Android实现音乐播放器歌词显示效果所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。