android lrc歌词显示

本文介绍了一个Android应用中实现LRC歌词显示的代码,包括LRCbean类的定义,LrcUtils类的解析方法,以及UI如何调用显示歌词。代码存在兼容性问题,作者提供了新版代码链接以修复这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  歌词双行交叉显示,代码不完善,兼容性差,还有待改进,进入正文:

以下为旧版代码,新版lrc解析代码已经写完,全面修复兼容性,请直接参考:http://www.qylk.blog.163.com/blog/static/134687356201162023117621/

先定义一个存放每句歌词的类,它定义了每句歌词的起始时间,持续时间,及每句歌词正文,相当于C语言的结构体。

public class LRCbean {
 private int beginTime=0;
 public int getBeginTime() {
  return beginTime;

 private int  lineTime=0;
 private String lrcBody = null;
 }
 public void setBeginTime(int beginTime) {
  this.beginTime = beginTime;
 }
 public int getLineTime() {
  return lineTime;
 }
 public void setLineTime(int lineTime) {
  this.lineTime = lineTime;
 }
 public String getLrcBody() {
  return lrcBody;
 }
 public void setLrcBody(String lrcBody) {
  this.lrcBody = lrcBody;
 }
}

 


歌词解析类如下:

public class LrcUtils {
 private  TreeMap<Integer, LRCbean> read(File path){//读取歌词
  TreeMap<Integer, LRCbean> lrc_read = new TreeMap<Integer, LRCbean>();//临时表
     String data =null;
     BufferedReader br = null;
     FileInputStream stream = null;
  try {
   stream = new FileInputStream(path);//读入歌词文件流
            br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));//向内存写入
  } catch (Exception e) {}
  try {
   while((data=br.readLine())!=null){//逐行读取
    if (data.length()>6){//此行有歌词
     if (data.charAt(3)==':'&&data.charAt(6)=='.'){//从歌词正文开始
      data = data.replace("[", "");
      data = data.replace("]", "@");
      data = data.replace(".", ":");
      String lrc[] = data.split("@");//将时间与歌词分开
      String lrcContent= null;
      if (lrc.length==2){//是正规的lrc文件,即每行只定义一句歌词
       lrcContent = lrc[lrc.length-1];//歌词正文
      }else{//非正规,抛弃此行
       lrcContent = "";
      }
      if(lrcContent.equals("")) lrcContent="Mucic..."; //此行歌词为空,添加提示信息
      String lrcTime[] = lrc[0].split(":");//计算开始结束时间
      int m = Integer.parseInt(lrcTime[0]);//分
      int s = Integer.parseInt(lrcTime[1]);//秒
      int ms = Integer.parseInt(lrcTime[2]);//毫秒
      
      int begintime = (m*60 + s) * 1000 + ms;//转换成毫秒
      LRCbean lrcbean = new LRCbean();
      lrcbean.setBeginTime(begintime);//设置歌词开始时间
      lrcbean.setLrcBody(lrcContent);//设置歌词内容
      lrc_read.put(begintime,lrcbean);//装载入表
     }
    }
   }
   stream.close();
   br.close();
  } catch (Exception e) {}
  //计算每句歌词需要的时间
  Iterator<Integer> iterator = lrc_read.keySet().iterator();//迭代器
  LRCbean oldval = null;
  int i = 0;
  TreeMap<Integer, LRCbean> lrc_map=new TreeMap<Integer, LRCbean>();
  while (iterator.hasNext()){//逐行处理,加入起始、持续时间
   Object ob = iterator.next();
   LRCbean val = lrc_read.get(ob);
   if (oldval==null){
    oldval = val;
   } else{
    LRCbean item1 = new LRCbean();//相当于每句歌词的结构体,包含次句歌词信息
    item1 = oldval;
    item1.setLineTime(val.getBeginTime()-oldval.getBeginTime());//持续时间
    lrc_map.put(new Integer(i), item1);//正式装载入歌词表
    i++;
    oldval = val;
   }
  }
  return lrc_map;
    }
 public TreeMap<Integer, LRCbean> check_lrc(String mname){
  String tem[]=mname.split("\\.");       //分离出例如"小三.mp3"中的"小三"
  File lrc=new File("mnt/sdcard/music/"+tem[0]+".lrc");//组装歌词路径,这里的路径你自己提供
  if(lrc.exists()){ //查询歌词文件是否存在
   TreeMap<Integer, LRCbean> lrcm=read(lrc);//读歌词
   return lrcm;
  }
  else return null;
 }
 
public int find_lrc_index(Integer cur_pos,TreeMap<Integer, LRCbean> Lrcmap){  //当拖动进度条,需要重新查询歌词的位置,输入参数是进度,及歌词表,返回查询到的歌词在表中的索引
 Iterator<Integer> iterator=Lrcmap.keySet().iterator();//定义迭代器
 int pos=0;
 while(iterator.hasNext()){
  pos = iterator.next();
     LRCbean val = Lrcmap.get(pos);
     if (val!=null){
         if (cur_pos<val.getBeginTime()){
          break;//查询到了索引,跳出循环
         }
     }
 }
 if(pos==0) return pos;
 return pos-1;//送回查询到的歌词索引
}

}

 

UI调用:配合进度条使用,在进度条进度发生改变时刷新歌词显示:

原理:事先将lrc歌词解析后,每句存入一个歌词表中,表中每行记录着这句歌词的相关信息,根据每句歌词的持续时间,将其等分为500us的段,将段数记为j,将进度条设置为每500us更新一次,这样每次将j减1,减到0时,则时间到,该显示下句歌词了。当改变进度条,需要重新查询歌词的位置,然后将i置为查询到的歌词索引值即可。i为当前歌词索引,切记i、j均为全局变量。

 

先注部分对象名:

(      private SeekBar bar;//进度条

          private TextView time,dura;//显示歌曲当前时间,总时间

          private TextView Lrc,Lrc2;//双行歌词

          private TreeMap<Integer, LRCbean> lrc_map = new TreeMap<Integer, LRCbean>();//歌词表

         private int i=0,j=0;//

        private LrcUtils  lrcUtils=new LrcUtils();

        private boolean haslrc=false;//歌词存在标志

        private boolean lrcwait=true;//歌词等待标志

        //用到的其余成员不写了,请自行判断

        )

 

 

bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {  //(进度条位置发生改变事件)
      @Override 
      public void onProgressChanged(SeekBar seekBar, int progress, 
        boolean fromUser) {
       time.setText(String.format("%02d:%02d", progress/60000,progress%60000/1000).toString());//更新显示时间进度
      try {
       if(haslrc==true && lrcwait==false){//有歌词并且等待标志为false
        while(j--<=0){//   j大约每500us减1,进度条更新需要一个线程控制,这里的500us就是进度条的更新周期,一定要与之一致

         LRCbean val2;

//下面的代码逻辑较难理解,我写成这样,你只管用就行了,为防止歌词显示异常,加了一些必要条件处理
         if(i==lrc_map.size()) {lrcwait=true;break;}//显示过了最后一条歌词,应该退出,lrcwait设为true,歌词将不再更新
         LRCbean val = lrc_map.get(i++);//获取当前歌词
         if(i==lrc_map.size()) {val2=new LRCbean();val2.setLrcBody("");}//到了结尾,新增一个结尾
         else val2 = lrc_map.get(i);//取出下一条歌词
         Integer linetime=val.getLineTime();//取持续时间
         if(i==1) {Lrc.setText(val.getLrcBody());}//初始化首句歌词
         if(i%2!=0) {Lrc2.setText(val2.getLrcBody());//偶数行
           Lrc2.setTextColor(Color.WHITE);
           Lrc.setTextColor(Color.GREEN);}
         else {Lrc.setText(val2.getLrcBody());//奇数行
           Lrc.setTextColor(Color.WHITE);
           Lrc2.setTextColor(Color.GREEN);}
         linetime-=mPlayer.getCurrentPos()-val.getBeginTime();

//实际等待时间,这里mplayer.getcurrentpos是取得歌曲现在进度,这是我自定义的方法,你改成你的mPlayer.getCurrentPosition()之类的。
         j=(int)linetime/500; //置等待计数器j
        }
       }       
      } catch (Exception e) {}
      } 

@Override 
      public void onStartTrackingTouch(SeekBar seekBar) {  //开始拖拉进度条
       wait=true; //进度条更新线程"暂停",
      } 
      @Override 
      public void onStopTrackingTouch(SeekBar seekBar) {  //停止拖拉进度条
       try{
        mPlayer.setCurrent(seekBar.getProgress());//歌曲进度调整,mPlayer.setCurrent改为你的mPlayer.seekTo(pos)之类的
        if(haslrc) set_lrc_pos();//歌词进度调整,set_lrc_pos此方法下面有定义,haslrc是歌词存在的标志
        if(mPlayer.isPlaying()) threadresume(); //进度条线程恢复,歌词继续更新
               }
               catch (RemoteException e) {}
      }});

 

当手动改变歌词进度,歌词需要同步更新,歌词显示调整

private void set_lrc_pos()  {
    LrcUtils lrcUtils=new LrcUtils();//歌词业务类,上面有定义过
    i=lrcUtils.find_lrc_index(bar.getProgress(),lrc_map);//寻找当前歌词索引
    LRCbean val2;
    LRCbean val = lrc_map.get(i++);//读取当前歌词(注意这里i加1了)
    if(i!=lrc_map.size()){//找到的不是结尾
    val2 = lrc_map.get(i);//读取下一条歌词
    }
    else {//恰好为结尾
     val2=new LRCbean();//新增一个空白结尾" ",(看不见的)
        val2.setLrcBody(" ");
    }
    int linetime=val.getLineTime();//读取持续时间
     if(i%2!=0){//偶数行歌词(注意这里i是加1后的i,实际上加1之前为偶数)
      Lrc.setText(val.getLrcBody());//显示歌词
      Lrc.setTextColor(Color.GREEN);
      Lrc2.setText(val2.getLrcBody());
      Lrc2.setTextColor(Color.WHITE); 
        }
     else{
      Lrc2.setText(val.getLrcBody());
      Lrc2.setTextColor(Color.GREEN);
      Lrc.setText(val2.getLrcBody());
      Lrc.setTextColor(Color.WHITE); 
        }
    linetime  -=mPlayer.getCurrentPos()-val.getBeginTime();

//计算实际要等待的时间,使其更加精确。mPlayer.getCurrentPos改成你的mPlayer.getCurrentPosition()之类的
    j=(int)linetime/500;//转换成等待计数器的值
              }

 

 

 

//进度条更新线程,记得在适当的时候开启这个线程

thread = new Thread(){
         @Override
         public void run() {
          while(! thread_des){ // thread_des你就设为false就行了,设为死循环。
           try {
         sleep(500);//睡500u秒                 
         if(wait==false){//wait是用于主进程控制该线程是否暂停更新进度条的标志
            bar.setProgress(mPlayer.getCurrentPos());

//设置当前播放的进度值,mPlayer.getCurrentPos改成你的mPlayer.getCurrentPosition()。
    }
            }catch (Exception e)
        {Log.i("tag", "ThreadError");} 
      }}}; 

 

 

 //检查歌词,每当换歌的时候调用

private void check_lrc(){
 lrc_map=null;
 lrc_map=lrcUtils.check_lrc(curr_name);

//检查是否存在,输入参数为歌曲名,如“小三.mp3”, curr_name改为你自己的。
 if(lrc_map ! =null){//存在
  i=0;j=0;//参数初始化
  haslrc=true;//歌词存在标志
 }
 else {haslrc=false;Lrc.setText("无歌词");Lrc.setTextColor(Color.GREEN);}
}

 这是我写的一个音乐播放器里的部分代码,完整代码请参阅http://www.qylk.blog.163.com/blog/static/134687356201162023117621/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值