边播放歌曲边滚动歌词
一、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
边播放歌曲边滚动歌词相关推荐
- 音乐播放器的滚动歌词的实现
先说下思路:歌词是tableView的实现,用个定时器每隔0.5秒就自动滚动一次,比较当前的时间和播放器的时间的大小来判断是否滚动到下一行.废话不多说,直接上代码. 一. 播放界面VC.h #impo ...
- .lrc java解析_lrc滚动歌词解析及显示
lrc歌词格式是什么样的? lrc是英文lyric(歌词)的缩写,被用做歌词文件的扩展名.以lrc为扩展名的歌词文件可以在各类数码播放器中同步显示.LRC 歌词是一种包含着":"形 ...
- 仿网易云网页版音乐播放器,实现歌词随歌曲进行滚动高亮
引言 前几天在使用网易云网页版听歌时,看着那个页面的歌词随歌曲进行高亮,突然也想自己手动地去实现一下,于是呢,就仿照了网易云音乐的网页自己也写了个页面.效果图如下: 当然了,此处不做css的样式介绍, ...
- Android应用开发--MP3音乐播放器滚动歌词实现
Android应用开发--MP3音乐播放器滚动歌词实现 2013年6月2日 简.美音乐播放器开发记录 -----前话 有网友给我博客评论说,让我借鉴好的Android代码,代码贴出来的时候最好整体先 ...
- android开发歌词滑动效果_Android应用开发--MP3音乐播放器滚动歌词实现
[android]代码库2013年6月2日 简.美音乐播放器开发记录 -----主题 这篇博客的主题是:"滚动歌词的实现" 要的效果如下: ----实现过程 1. 建立歌词内容实体 ...
- 全网首个tk网络音乐播放器!支持歌词滚动!你看我吊不?
这是一个用 Python 的 tkinter 库做的一个网络音乐播放器.我不说它的 UI 设计的有多好看,但是它的功能绝对是全站首个!坚持看到底,你不点赞算我输! 成果展示 程序截图 前期准备 程序结 ...
- android MP3播放器(支持歌词滚动等功能)
大二课余时间写的音乐播放器,发现目前网上很多android播放器都缺胳膊少腿的, 于是便分享出来给才接触android的同学作参考,(Tomcat服务器功能已删除) 实现了启动动画,引导界面,appw ...
- Android应用开发 MP3音乐播放器滚动歌词实现
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! Andr ...
- JavaScript实时监听歌曲播放进度显示对应歌词
前言 在之前我就想试试在线的音乐播放器的制作,昨晚动手实现了播放音乐的歌词实时对应显示的组件,下面就来看看其中的解析原理. 正文 这里我以李玉刚的<刚好遇见你>为例,首先我们需要获取到音频 ...
最新文章
- 181个NLP教程合集,Colab一键直达,无需环境配置,此外还有481个文本数据集
- 使用PHP处理POST上传时$_FILES数组为何为空
- HDU 1874 畅通工程续
- 【30集iCore3_ADP出厂源代码(ARM部分)讲解视频】30-8底层驱动之RTC
- css盒子模型_css的盒子模型是什么
- haskell程序设计语言
- oc73--NSArray使用
- System.Windows.Forms.ListView
- bootstrap ie兼容
- 如何制作自己的网课网站 需网课查课插件
- linux安装vim不成功,centos安装vim失败解决
- 博图软件的C语言脚本,wincc v13 博图软件里如何写脚本程序
- Mac 锁屏防止断网、睡眠、注销登录
- AI音箱工作原理浅析
- 使用 CNN 进行面部表情检测
- azure java sdk_使用 Azure SDK for Java
- LCD液晶屏中文显示介绍
- mmsegmentation安装过程报错记录
- js实现好看的图案 加勒比海盗(php拍黄片)
- linux下运行omnet,Linux下OMNet++安装步骤[原创]