android通过绑定service实现歌曲播放,并把歌词以不同色调动态绘出

来源:互联网 发布:商家服务规范 淘宝 编辑:程序博客网 时间:2024/05/16 01:16
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;}@Overridepublic 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;//记录文本的sizeprivate 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);}//绘制歌词@Overrideprotected 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 4for(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:63statement = 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:43String 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*20size = (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";@Overridepublic void onCreate() {System.out.println("PlayService.onCreate()");player = new MediaPlayer();}@Overridepublic void onStart(Intent intent, int startId) {System.out.println("PlayService.onStart()");}@Overridepublic 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{@Overridepublic void start() {try {//重新播放。player.reset();player.setDataSource("/mnt/sdcard/tz_player/"+songName);player.prepare();player.start();} catch (Exception e) {e.printStackTrace();}}@Overridepublic void resume() {player.start();}@Overridepublic void pause() {if(player.isPlaying()){player.pause();}}@Overridepublic void stop() {player.reset();}@Overridepublic int getCurrentPosition() {// TODO Auto-generated method stubreturn player.getCurrentPosition();}@Overridepublic boolean isPlaying() {// TODO Auto-generated method stubreturn player.isPlaying();}@Overridepublic int getMusicDuration() {// TODO Auto-generated method stubreturn player.getDuration();}@Overridepublic void setPosition(int position) {//seek跳转 拖动player.seekTo(position);}}@Overridepublic void onDestroy() {super.onDestroy();System.out.println("PlayService.onDestroy()");//回收player资源。if(player!=null){player.reset();player.release();}}@Overridepublic 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 {@Overridepublic void onServiceConnected(ComponentName name, IBinder binder) {// TODO Auto-generated method stubiplay = (PlayService.IPlay) binder;}@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stub}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initContent();initService();initThread();initListener();}private void initListener() {sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {// TODO Auto-generated method stub}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {// TODO Auto-generated method stub}//拖动过程中,会触发该方法。@Overridepublic 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);//歌曲正在播放,才更新sbif(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,一定要解绑。@Overrideprotected void onDestroy() {super.onDestroy();isExit = true;if (myConn != null) {unbindService(myConn);}}}


整理自教程




0 0
原创粉丝点击