android通过绑定service实现歌曲播放,并把歌词以不同色调动态绘出
2016-03-06 19:49
686 查看
public class SongInfo { private String key; private String value; public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public SongInfo(String key, String value) { super(); this.key = key; this.value = value; } public SongInfo() { // TODO Auto-generated constructor stub } }
public class Lyric { private Integer index; private Integer beginTime; private Integer endTime; private String content; public Integer getIndex() { return index; } public void setIndex(Integer index) { this.index = index; } public Integer getBeginTime() { return beginTime; } public void setBeginTime(Integer beginTime) { this.beginTime = beginTime; } public Integer getEndTime() { return endTime; } public void setEndTime(Integer endTime) { this.endTime = endTime; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Lyric() { // TODO Auto-generated constructor stub } public Lyric(Integer index, Integer beginTime, Integer endTime, String content) { super(); this.index = index; this.beginTime = beginTime; this.endTime = endTime; this.content = content; } @Override public String toString() { return "Lyric [index=" + index + ", beginTime=" + beginTime + ", endTime=" + endTime + ", content=" + content + "]"; } }
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeMap; import java.util.Map.Entry; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.os.Environment; import android.util.AttributeSet; import android.view.View; public class LyricView1 extends View { private int height; private int width; private int cx; private int cy; //高亮画笔,以及 普通画笔 private Paint paint_h; private Paint paint_n; //记录当前歌词对象 private Lyric currentLyric; //记录当前歌曲的所有歌词信息。 private TreeMap<Integer, Lyric> lyric_map; //记录文本的size private int size; //记录歌词之间的间距。 private int gap = 10; private int currentPosition; public LyricView1(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); } private void initPaint() { //初始化 paint_h = new Paint(); paint_n = new Paint(); //设置画笔颜色 paint_h.setColor(Color.RED); paint_n.setColor(Color.BLACK); //设置文本对齐方式 paint_h.setTextAlign(Align.CENTER); paint_n.setTextAlign(Align.CENTER); } //绘制歌词 @Override protected void onDraw(Canvas canvas) { //初始化界面尺寸。 if(width == 0){ initSize(); } //绘制歌词。 if(currentLyric!=null){ //开始绘制中心歌词 int currentIndex = currentLyric.getIndex(); int beginTime = currentLyric.getBeginTime(); int endTime = currentLyric.getEndTime(); int duration = endTime - beginTime; //获得当前的时间,然后用当前时间-起始时间。 int currentDuration = currentPosition-beginTime; //为了实现歌词的向上移动,计算当前时间点,应该往上面走的距离。 double ratio = 1.0*currentDuration/duration; double distance = ratio*(size+gap); int currentY = (int)(cy-distance); paint_h.setColor(Color.RED); canvas.drawText(currentLyric.getContent(), cx, currentY, paint_h); //计算有效区域。 计算有效区域左上角的坐标 以及 左下角的坐标。 //获得中心歌词文本的宽度 int contentLength = (int)paint_h.measureText(currentLyric.getContent()); //获得有效区域的宽度。 int availableSpanWidth = (int)(ratio*contentLength); //左上角的坐标。 int left = cx - contentLength/2; int top = (int)(currentY + paint_h.ascent()); //右下角 int right = left + availableSpanWidth; int bottom =(int)(currentY + paint_h.descent()); //根据左上角点和右下角的点,确定当前的有效区域。 canvas.save(); canvas.clipRect(left, top, right, bottom); //因为我们现在要绘制蓝色的中心歌词。 paint_h.setColor(Color.BLUE); canvas.drawText(currentLyric.getContent(), cx, currentY, paint_h); canvas.restore(); //绘制已经唱过的歌词 // lyric_map<Integer> 1 2 3 4 for(int j= currentIndex-1;j >= 1; j--){ //判断当前的歌词,是否会完全超出顶部。 //求当前歌词的基点坐标。 int x = cx; int y = cy - (currentIndex-j)*(size + gap); //只有当歌词完全看不见的时候,才不会绘制当前歌词 if(y<-paint_n.descent()){ //当前歌词绘制出来,是一点都看不见,所以不会只。 }else{ String content = lyric_map.get(j).getContent(); canvas.drawText(content, x, (int)(y-distance), paint_n); } } //绘制还没有唱过的歌词。 for( int k = currentIndex+1;k<=lyric_map.size();k++ ){ //计算当前歌词的坐标 int x = cx; int y = cy + (k-currentIndex)*(size + gap); //abs absolute 绝对 绝对值 if( y > (height + Math.abs(paint_h.ascent()) ) ){ //说明当前绘制的歌词已经超出了底部。 }else{ String content = lyric_map.get(k).getContent(); canvas.drawText(content, x, (int)(y-distance), paint_n); } } } } private void initSize() { width = getWidth(); height = getHeight(); cx = width/2; cy = height/2; } //传入当前播放时间,获得对应的歌词文件。 public void getCurrentLyric(int currentPosition){ this.currentPosition = currentPosition; if(lyric_map!=null){ Set<Entry<Integer, Lyric>> entrySet = lyric_map.entrySet(); for (Entry<Integer, Lyric> entry : entrySet) { Lyric lyric = entry.getValue(); if( currentPosition>=lyric.getBeginTime() && currentPosition<=lyric.getEndTime() ){ currentLyric = lyric; } } } } //传入歌词文件的名称,然后解析获得歌词信息。 public void readLyric(String lyricFileName){ File file = new File( Environment.getExternalStorageDirectory().getAbsolutePath() +"/tz_player/"+lyricFileName ); if(file.exists() && file.isFile()==true){ //开始解析file文件 lyric_map = new TreeMap<Integer, Lyric>(); //定义歌词的下标 int index = 1; //保存多条歌曲信息。 List<SongInfo> infoList = new ArrayList<SongInfo>(); try { FileInputStream fis = new FileInputStream(file); BufferedReader reader = new BufferedReader( new InputStreamReader(fis, "GBK")); //一行一行解析歌词文件。 String line = null; while( (line = reader.readLine()) != null){ //开始解析有效的歌词信息。 /* * 句子的分类: * 1,只有[] 括起来的句子 * a. * [ti:原来爱情这么伤] * ti:原来爱情这么伤] * ti:原来爱情这么伤@ * b.特殊的歌词信息,只有时间,内容是空。 * [00:09.63] * 00:09.63] * 00:09.63@ * * 2,有[],[]后面还有歌词信息。 * [00:05.43]作词:彭学斌 * 00:05.43]作词:彭学斌 * 00:05.43@作词:彭学斌 * */ line = line.replaceAll("\\[", ""); line = line.replaceAll("\\]", "@"); //利用@,将字符串进行撕裂。 String[] array = line.split("@"); //如果array长度是1,说明当前句子是 1类型的句子;反之是2类型的句子。 if( array.length==1 ){ String statement = array[0]; //statement ti:原来爱情这么伤 00:09:63 statement = statement.replaceAll("\\.", ":"); String[] array1 = statement.split(":"); if( array1.length==2 ){ SongInfo songInfo = new SongInfo(array1[0], array1[1]); infoList.add(songInfo); }else if( array1.length==3 ){ Lyric lyric = new Lyric(); lyric.setContent(""); lyric.setIndex(index); //获得起始时间。 int minute = Integer.parseInt(array1[0]); int second = Integer.parseInt(array1[1]); int mil = Integer.parseInt(array1[2]); int beginTime = minute*60*1000 + second*1000 + mil*10; lyric.setBeginTime(beginTime); lyric_map.put(index, lyric); index++; } }else if( array.length==2 ){ //00:05.43@作词:彭学斌 Lyric lyric = new Lyric(); String content = array[1]; lyric.setContent(content); //time 00:05:43 String time = array[0]; time = time.replaceAll("\\.", ":"); String[] array1=time.split(":"); //获得起始时间。 int minute = Integer.parseInt(array1[0]); int second = Integer.parseInt(array1[1]); int mil = Integer.parseInt(array1[2]); int beginTime = minute*60*1000 + second*1000 + mil*10; lyric.setBeginTime(beginTime); lyric.setIndex(index); lyric_map.put(index, lyric); index++; } } //为了获得当前歌词的截止时间,必须知道后一句歌词的截止时间。 //获得 2-最后一句歌词。 for(int i=2;i <= lyric_map.size();i++){ //i 2 3 4 5 6... Lyric current = lyric_map.get(i); Lyric pre = lyric_map.get(i-1); pre.setEndTime( current.getBeginTime()-1 ); } } catch (Exception e) { e.printStackTrace(); } } } public void getMaxSize() { //1,获得歌词文件中最长歌词的字符数量。 if(lyric_map==null){ size = 30;//默认值 }else{ int length = lyric_map.get(1).getContent().length(); for(int i=2;i<=lyric_map.size();i++){ int currentLength = lyric_map.get(i).getContent().length(); if(length < currentLength){ length = currentLength; } } //2,我们希望LyricView左右两边留下20px,那么留给文本的宽度就是 LyricView.width - 2*20 size = (width-2*20)/length; } //设置给画笔 paint_h.setTextSize(size); paint_n.setTextSize(size); } }
import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.IBinder; public class PlayService extends Service{ private MediaPlayer player; private String songName = "mySong.mp3"; private String lyricName = "mySong.txt"; @Override public void onCreate() { System.out.println("PlayService.onCreate()"); player = new MediaPlayer(); } @Override public void onStart(Intent intent, int startId) { System.out.println("PlayService.onStart()"); } @Override public IBinder onBind(Intent intent) { System.out.println("PlayService.onBind()"); return new MyBinder(); } public interface IPlay{ //播放 重新播放、继续。 //重新播放 void start(); //继续 void resume(); //暂停 void pause(); //停止 void stop(); //返回当前播放的时间。 int getCurrentPosition(); //返回歌曲是否正在播放 boolean isPlaying(); //获得音乐长度 int getMusicDuration(); //设置媒体播放器进度。 void setPosition(int position); } private class MyBinder extends Binder implements IPlay{ @Override public void start() { try { //重新播放。 player.reset(); player.setDataSource("/mnt/sdcard/tz_player/"+songName); player.prepare(); player.start(); } catch (Exception e) { e.printStackTrace(); } } @Override public void resume() { player.start(); } @Override public void pause() { if(player.isPlaying()){ player.pause(); } } @Override public void stop() { player.reset(); } @Override public int getCurrentPosition() { // TODO Auto-generated method stub return player.getCurrentPosition(); } @Override public boolean isPlaying() { // TODO Auto-generated method stub return player.isPlaying(); } @Override public int getMusicDuration() { // TODO Auto-generated method stub return player.getDuration(); } @Override public void setPosition(int position) { //seek跳转 拖动 player.seekTo(position); } } @Override public void onDestroy() { super.onDestroy(); System.out.println("PlayService.onDestroy()"); //回收player资源。 if(player!=null){ player.reset(); player.release(); } } @Override public boolean onUnbind(Intent intent) { System.out.println("PlayService.onUnBind()"); return super.onUnbind(intent); } }
import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; public class MainActivity extends Activity { private SeekBar sb; private MyConnection myConn; private PlayService.IPlay iplay; private boolean isExit = false; private LyricView1 lyricView; private class MyConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder binder) { // TODO Auto-generated method stub iplay = (PlayService.IPlay) binder; } @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initContent(); initService(); initThread(); initListener(); } private void initListener() { sb.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) { if(fromUser==true){ //将媒体播放器,设置成当前拖动到的进度。 if(iplay!=null){ iplay.setPosition(progress); } } } }); } private void initThread() { new Thread(){ public void run() { while(isExit == false){ try { Thread.sleep(100); //歌曲正在播放,才更新sb if(iplay!=null && iplay.isPlaying()==true){ //实现seekBar的进度更新 if(sb.getMax()==100){ sb.setMax(iplay.getMusicDuration()); } //获得当前播放的时间,然后设置给sb。 int currentPostion = iplay.getCurrentPosition(); sb.setProgress(currentPostion); //还应该刷新界面的歌词。 //1,先获得当前播放点(59.8s),所对应的歌词对象。 int currentTime = iplay.getCurrentPosition(); //2,根据当前时间,获得对应的歌词文件。 lyricView.getCurrentLyric(currentTime); //3,刷新LyricView,将最新歌词的内容绘制出来。 //lyricView.onDraw(canvas)? mHandler.sendEmptyMessage(1); } } catch (Exception e) { e.printStackTrace(); } } }; }.start(); } private Handler mHandler = new Handler(){ public void handleMessage(android.os.Message msg) { lyricView.invalidate(); }; }; private void initContent() { sb = (SeekBar)findViewById(R.id.sb); lyricView = (LyricView1)findViewById(R.id.lyricView); } private void initService() { Intent intent = new Intent(MainActivity.this, PlayService.class); // 首先start服务,然后bind服务。 目的是让service承担两个责任。 startService(intent); myConn = new MyConnection(); bindService(intent, myConn, Context.BIND_AUTO_CREATE); } // 按钮的点击事件。 public void play(View v) { if(iplay!=null){ //开始播放新歌曲。 iplay.start(); //获得当前 播放的歌曲总的时间。 int duration = iplay.getMusicDuration(); sb.setMax(duration); //点击播放,开始播放一首新歌曲,应该找到并且解析当前歌曲所对应的歌词文件。 lyricView.readLyric("mySong.txt"); //获得此时的歌词文字的最大值。 lyricView.getMaxSize(); } } public void pause(View v) { if(iplay!=null){ iplay.pause(); } } public void resume(View v) { if(iplay!=null){ iplay.resume(); } } public void stop(View v) { if(iplay!=null){ iplay.stop(); } } // 按back键,退出当前Activity,一定要解绑。 @Override protected void onDestroy() { super.onDestroy(); isExit = true; if (myConn != null) { unbindService(myConn); } } }
整理自教程
相关文章推荐
- Android学习之解决多个Fragment切换时重新实例化的问题
- Android四大组件之Service
- 关于Android MultiDex的问题
- Android 热修复Nuwa的原理及Gradle插件源码解析
- 浅谈Android开发中内存泄露与优化
- Android View 的弹性滑动: Scroller使用说明
- 盘点Android应用开发中曾经很流行但是已经过时或即将过时的技术
- Android 通过JNI实现守护进程,使Service服务不被杀死
- 2016-3-5
- android多屏幕适应性
- Android开发之Animation介绍(下) ——Property Animation
- Android Service完全解析,关于服务你所需知道的一切(下)
- Android InputStream等通用转化总结 初阶篇
- Android Studio NDK环境搭建
- android service 后台执行定时任务
- 基于Android的Word在线预览
- android简单实例---------ActionBar的简单使用(三,补充)
- Android的错误
- 从Android运行时出发,打造我们的脱壳神器
- Android中的context的学习理解