一、LyricView

package com.ct.lrc;import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;import android.R.integer;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;public class LyricView extends View{private Context context;private float offsetY;      //歌词在Y轴上的偏移量,此值会根据歌词的滚动变小 private static TreeMap<Integer, LyricObject> lrc_map;private static boolean blLrc=false;private float mX;       //屏幕X轴的中点,此值固定,保持歌词在X中间显示  Paint paint = new Paint();Paint paintHL = new Paint();private  int SIZEWORD=0;//显示歌词文字的大小值  private  int INTERVAL=45;//歌词每行的间隔  private int lrcIndex=0; //保存歌词TreeMap的下标  private float touchY;   //当触摸歌词View时,保存为当前触点的Y轴坐标  private float touchX;  private boolean blScrollView=false;  public LyricView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();// TODO Auto-generated constructor stub
    }public LyricView(Context context, AttributeSet attrs) {super(context, attrs);init();// TODO Auto-generated constructor stub
    }public LyricView(Context context) {super(context);init();// TODO Auto-generated constructor stub
    }private void init(){lrc_map = new TreeMap<Integer, LyricObject>();offsetY = 320;paint = new Paint();paint.setAlpha(180);paint.setColor(Color.GREEN);paint.setTextAlign(Align.CENTER);paint.setDither(true);paint.setAntiAlias(true);paintHL = new Paint();paintHL.setAlpha(255);paintHL.setColor(Color.RED);paintHL.setTextAlign(Align.CENTER);paintHL.setDither(true);paintHL.setAntiAlias(true);}@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stubif(blLrc){//歌词存在的情况
            paint.setTextSize(SIZEWORD);paintHL.setTextSize(SIZEWORD);LyricObject temp = lrc_map.get(lrcIndex);canvas.drawText(temp.getLrc(),mX, offsetY+(SIZEWORD+INTERVAL)*lrcIndex, paintHL);// 画当前歌词之前的歌词  for(int i = lrcIndex - 1;i>0;i--){temp = lrc_map.get(i);if(offsetY+(SIZEWORD+INTERVAL)*i<0){  break;  } canvas.drawText(temp.getLrc(), mX, offsetY+(SIZEWORD+INTERVAL)*i, paint);}for(int i = lrcIndex + 1;i<lrc_map.size();i++){temp = lrc_map.get(i);if(offsetY+(SIZEWORD+INTERVAL)*i>600){  break;  } canvas.drawText(temp.getLrc(),mX, offsetY+(SIZEWORD+INTERVAL)*i, paint);} }else {paint.setTextSize(25);canvas.drawText("找不到歌词", mX, 310, paint);}super.onDraw(canvas);}@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubfloat tt = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:touchX = event.getX();break;case MotionEvent.ACTION_MOVE:touchY = tt - touchY;offsetY = touchY + offsetY;break;case MotionEvent.ACTION_UP:blScrollView = false;break;default:break;}touchY = tt;return true;}/** * 根据歌词里面最长的那句来确定歌词字体的大小 **/public void SetTextSize(){if(!blLrc){return;}int max = lrc_map.get(0).getLrc().length();for(int i=1;i<lrc_map.size();i++){LyricObject temp = lrc_map.get(i);int length = temp.getLrc().length();if(max < length){max = length;}}SIZEWORD=320/max; }@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {// TODO Auto-generated method stubmX = w * 0.5f; super.onSizeChanged(w, h, oldw, oldh);}/** *  歌词滚动的速度 *  * @return 返回歌词滚动的速度 */public float SpeedLrc(){float speed = 0;if(offsetY+(SIZEWORD+INTERVAL)*lrcIndex>220){  speed=((offsetY+(SIZEWORD+INTERVAL)*lrcIndex-220)/20);  } else if(offsetY+(SIZEWORD+INTERVAL)*lrcIndex < 120){  Log.i("speed", "speed is too fast!!!");  speed = 0;  }  return speed;}/** * 按当前的歌曲的播放时间,从歌词里面获得那一句 * @param time 当前歌曲的播放时间 * @return 返回当前歌词的索引值 */public int SelectIndex(int time){if(!blLrc){return 0;}int index = 0;for(int i=0;i<lrc_map.size();i++){LyricObject temp =lrc_map.get(i);if(temp.getBegintime() < time){++index;}}lrcIndex = index -1;if(lrcIndex < 0){lrcIndex = 0;}return lrcIndex;}/** * 读取歌词文件 * @param file 歌词的路径 *  */  public static void read(String file) {  TreeMap<Integer, LyricObject> lrc_read =new TreeMap<Integer, LyricObject>();  String data = "";  try {  File saveFile=new File(file);  // System.out.println("是否有歌词文件"+saveFile.isFile());  if(!saveFile.isFile()){  blLrc=false;  return;  }  blLrc=true;  //System.out.println("bllrc==="+blLrc);  FileInputStream stream = new FileInputStream(saveFile);//  context.openFileInput(file);
            BufferedReader br = new BufferedReader(new InputStreamReader(stream,"GB2312"));     int i = 0;  Pattern pattern = Pattern.compile("\\d{2}");  while ((data = br.readLine()) != null) {     // System.out.println("++++++++++++>>"+data);  data = data.replace("[","");//将前面的替换成后面的  data = data.replace("]","@");  String splitdata[] =data.split("@");//分隔  if(data.endsWith("@")){  for(int k=0;k<splitdata.length;k++){  String str=splitdata[k];  str = str.replace(":",".");  str = str.replace(".","@");  String timedata[] =str.split("@");  Matcher matcher = pattern.matcher(timedata[0]);  if(timedata.length==3 && matcher.matches()){  int m = Integer.parseInt(timedata[0]);  //分  int s = Integer.parseInt(timedata[1]);  //秒  int ms = Integer.parseInt(timedata[2]); //毫秒  int currTime = (m*60+s)*1000+ms*10;  LyricObject item1= new LyricObject();  item1.setBegintime(currTime);  item1.setLrc("");  lrc_read.put(currTime,item1);  }  }  }  else{  String lrcContenet = splitdata[splitdata.length-1];   for (int j=0;j<splitdata.length-1;j++)  {  String tmpstr = splitdata[j];  tmpstr = tmpstr.replace(":",".");  tmpstr = tmpstr.replace(".","@");  String timedata[] =tmpstr.split("@");  Matcher matcher = pattern.matcher(timedata[0]);  if(timedata.length==3 && matcher.matches()){  int m = Integer.parseInt(timedata[0]);  //分  int s = Integer.parseInt(timedata[1]);  //秒  int ms = Integer.parseInt(timedata[2]); //毫秒  int currTime = (m*60+s)*1000+ms*10;  LyricObject item1= new LyricObject();  item1.setBegintime(currTime);  item1.setLrc(lrcContenet);  lrc_read.put(currTime,item1);// 将currTime当标签  item1当数据 插入TreeMap里  i++;  }  }  }  }   stream.close();  }  catch (FileNotFoundException e) {  }  catch (IOException e) {  }  /* * 遍历hashmap 计算每句歌词所需要的时间 */  lrc_map.clear();  data ="";  Iterator<Integer> iterator = lrc_read.keySet().iterator();  LyricObject oldval  = null;  int i =0;  while(iterator.hasNext()) {  Object ob =iterator.next();  LyricObject val = (LyricObject)lrc_read.get(ob);  if (oldval==null)  oldval = val;  else  {  LyricObject item1= new LyricObject();  item1  = oldval;  int delta = val.getBegintime() - oldval.getBegintime();item1.setTimeline(delta);lrc_map.put(new Integer(i), item1);  i++;  oldval = val;  }  if (!iterator.hasNext()) {  lrc_map.put(new Integer(i), val);  }  }  } /** * @return the blLrc */  public static boolean isBlLrc() {  return blLrc;  }  /** * @return the offsetY */  public float getOffsetY() {  return offsetY;  }  /** * @param offsetY the offsetY to set */  public void setOffsetY(float offsetY) {  this.offsetY = offsetY;  }  /** * @return 返回歌词文字的大小 */  public int getSIZEWORD() {  return SIZEWORD;  }  /** * 设置歌词文字的大小 * @param sIZEWORD the sIZEWORD to set */  public void setSIZEWORD(int sIZEWORD) {  SIZEWORD = sIZEWORD;  }}

二、布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:background="#FFFFFF" ><com.ct.lrc.LyricViewandroid:id="@+id/mylrc"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_marginBottom="50dip"android:layout_marginTop="50dip" /><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:orientation="horizontal" ><Buttonandroid:id="@+id/button"android:layout_width="wrap_content"android:layout_height="wrap_content" /><SeekBarandroid:id="@+id/seekbarmusic"android:layout_width="205px"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_marginBottom="5px"android:progress="0" /></LinearLayout></RelativeLayout>

三、mainactivty

package com.ct.lrc;import java.io.IOException;import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;public class MainActivity extends Activity {/** Called when the activity is first created. */private LyricView lyricView;  private MediaPlayer mediaPlayer;  private Button miniStart;  private SeekBar seekBar;  private String mp3Path;  private int INTERVAL=45;//歌词每行的间隔
    @Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);mp3Path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/lyq.mp3";System.out.println("ct test------------->mp3Path  "+mp3Path);lyricView = (LyricView) findViewById(R.id.mylrc);  mediaPlayer = new MediaPlayer();  ResetMusic(mp3Path);SerchLrc(); lyricView.SetTextSize(); miniStart = (Button) findViewById(R.id.button);  miniStart.setText("播放");  seekBar = (SeekBar) findViewById(R.id.seekbarmusic);  seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {  @Override  public void onStopTrackingTouch(SeekBar seekBar) {  // TODO Auto-generated method stub
  }  @Override  public void onStartTrackingTouch(SeekBar seekBar) {  // TODO Auto-generated method stub
  }  @Override  public void onProgressChanged(SeekBar seekBar, int progress,  boolean fromUser) {  // TODO Auto-generated method stub  if (fromUser) {  mediaPlayer.seekTo(progress);  lyricView.setOffsetY(220 - lyricView.SelectIndex(progress)   * (lyricView.getSIZEWORD() + INTERVAL-1));  }  }  });  miniStart.setOnClickListener(new OnClickListener() {  @Override  public void onClick(View v) {  // TODO Auto-generated method stub  if (mediaPlayer.isPlaying()) {  miniStart.setText("播放");  mediaPlayer.pause();  } else {  miniStart.setText("暂停");  mediaPlayer.start();  lyricView.setOffsetY(220 - lyricView.SelectIndex(mediaPlayer.getCurrentPosition())  * (lyricView.getSIZEWORD() + INTERVAL-1));  }  }  });mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {  @Override  public void onCompletion(MediaPlayer mp) {  ResetMusic(mp3Path);  lyricView.SetTextSize();  lyricView.setOffsetY(200);  mediaPlayer.start();  }  });  seekBar.setMax(mediaPlayer.getDuration());  new Thread(new runable()).start(); }class runable implements Runnable {  @Override  public void run() {  // TODO Auto-generated method stub  while (true) {  try {  Thread.sleep(100);  if (mediaPlayer.isPlaying()) {  lyricView.setOffsetY(lyricView.getOffsetY() - lyricView.SpeedLrc());  lyricView.SelectIndex(mediaPlayer.getCurrentPosition());  seekBar.setProgress(mediaPlayer.getCurrentPosition());  mHandler.post(mUpdateResults);  }  } catch (InterruptedException e) {  // TODO Auto-generated catch block
                    e.printStackTrace();  }  }  }  }  Handler mHandler = new Handler();  Runnable mUpdateResults = new Runnable() {  public void run() {  lyricView.invalidate(); // 更新视图
        }  };  private void ResetMusic(String path){mediaPlayer.reset();  try {  mediaPlayer.setDataSource(mp3Path);  mediaPlayer.prepare();  } catch (IllegalArgumentException e) {  // TODO Auto-generated catch block
            e.printStackTrace();  } catch (IllegalStateException e) {  // TODO Auto-generated catch block
            e.printStackTrace();  } catch (IOException e) {  // TODO Auto-generated catch block
            e.printStackTrace();  }  }public void SerchLrc() {  String lrc = mp3Path;  lrc = lrc.substring(0, lrc.length() - 4).trim() + ".lrc".trim();  LyricView.read(lrc);  lyricView.SetTextSize();  lyricView.setOffsetY(350);  }
}

四、LyricObject

package com.ct.lrc;public class LyricObject {public int begintime; // 开始时间  public int endtime; // 结束时间  public int timeline; // 单句歌词用时  public String lrc; // 单句歌词
}

转载于:https://www.cnblogs.com/ct732003684/archive/2013/01/16/2862977.html

边播放歌曲边滚动歌词相关推荐

  1. 音乐播放器的滚动歌词的实现

    先说下思路:歌词是tableView的实现,用个定时器每隔0.5秒就自动滚动一次,比较当前的时间和播放器的时间的大小来判断是否滚动到下一行.废话不多说,直接上代码. 一. 播放界面VC.h #impo ...

  2. .lrc java解析_lrc滚动歌词解析及显示

    lrc歌词格式是什么样的? lrc是英文lyric(歌词)的缩写,被用做歌词文件的扩展名.以lrc为扩展名的歌词文件可以在各类数码播放器中同步显示.LRC 歌词是一种包含着":"形 ...

  3. 仿网易云网页版音乐播放器,实现歌词随歌曲进行滚动高亮

    引言 前几天在使用网易云网页版听歌时,看着那个页面的歌词随歌曲进行高亮,突然也想自己手动地去实现一下,于是呢,就仿照了网易云音乐的网页自己也写了个页面.效果图如下: 当然了,此处不做css的样式介绍, ...

  4. Android应用开发--MP3音乐播放器滚动歌词实现

    Android应用开发--MP3音乐播放器滚动歌词实现 2013年6月2日  简.美音乐播放器开发记录 -----前话 有网友给我博客评论说,让我借鉴好的Android代码,代码贴出来的时候最好整体先 ...

  5. android开发歌词滑动效果_Android应用开发--MP3音乐播放器滚动歌词实现

    [android]代码库2013年6月2日 简.美音乐播放器开发记录 -----主题 这篇博客的主题是:"滚动歌词的实现" 要的效果如下: ----实现过程 1. 建立歌词内容实体 ...

  6. 全网首个tk网络音乐播放器!支持歌词滚动!你看我吊不?

    这是一个用 Python 的 tkinter 库做的一个网络音乐播放器.我不说它的 UI 设计的有多好看,但是它的功能绝对是全站首个!坚持看到底,你不点赞算我输! 成果展示 程序截图 前期准备 程序结 ...

  7. android MP3播放器(支持歌词滚动等功能)

    大二课余时间写的音乐播放器,发现目前网上很多android播放器都缺胳膊少腿的, 于是便分享出来给才接触android的同学作参考,(Tomcat服务器功能已删除) 实现了启动动画,引导界面,appw ...

  8. Android应用开发 MP3音乐播放器滚动歌词实现

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! Andr ...

  9. JavaScript实时监听歌曲播放进度显示对应歌词

    前言 在之前我就想试试在线的音乐播放器的制作,昨晚动手实现了播放音乐的歌词实时对应显示的组件,下面就来看看其中的解析原理. 正文 这里我以李玉刚的<刚好遇见你>为例,首先我们需要获取到音频 ...

最新文章

  1. 181个NLP教程合集,Colab一键直达,无需环境配置,此外还有481个文本数据集
  2. 使用PHP处理POST上传时$_FILES数组为何为空
  3. HDU 1874 畅通工程续
  4. 【30集iCore3_ADP出厂源代码(ARM部分)讲解视频】30-8底层驱动之RTC
  5. css盒子模型_css的盒子模型是什么
  6. haskell程序设计语言
  7. oc73--NSArray使用
  8. System.Windows.Forms.ListView
  9. bootstrap ie兼容
  10. 如何制作自己的网课网站 需网课查课插件
  11. linux安装vim不成功,centos安装vim失败解决
  12. 博图软件的C语言脚本,wincc v13 博图软件里如何写脚本程序
  13. Mac 锁屏防止断网、睡眠、注销登录
  14. AI音箱工作原理浅析
  15. 使用 CNN 进行面部表情检测
  16. azure java sdk_使用 Azure SDK for Java
  17. LCD液晶屏中文显示介绍
  18. mmsegmentation安装过程报错记录
  19. js实现好看的图案 加勒比海盗(php拍黄片)
  20. linux下运行omnet,Linux下OMNet++安装步骤[原创]

热门文章

  1. 想要成为一名标准的数据分析师,需要看哪些书
  2. SpringCloud 学习笔记
  3. 当今最受欢迎的物联网协议(学习笔记)
  4. 【序列化】UNSAFE_DESERIALIZATION 不安全的反序列化和反序列化漏洞
  5. 剑指Offer66—构建乘积数组
  6. 满满干货赶紧进来!!!轻松掌握C语言中的函数
  7. 三坐标测量机测头的测球半径补偿误差
  8. 虚拟内存的设置并不麻烦,但是要用…
  9. 史上最详细的docker学习手册,请收藏!
  10. 双11天猫自动亲亲抢红包工具