您的位置:首页 > 移动开发 > Android开发

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);
}
}
}


整理自教程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: