本博文为子墨原创,转载请注明出处!
http://blog.csdn.net/zimo2013/article/details/16849695
1.示意图
2. 代码实现
/**
* MainActivity.java
* @author zimo2013
* @see http://blog.csdn.net/zimo2013
*
*/
public class MainActivity extends Activity implements OnClickListener,
OnSeekBarChangeListener {
private LrcSurfaceView surfaceView;
private Button btPlay;
private Button btPause;
private Button btStop;
private SeekBar bar;
private PlayerConn conn;
private IMyBinder binder;
private boolean isDrag; //进度条是否正在拖拽
private List<LrcInfo> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
list = new ArrayList<LrcInfo>();
conn = new PlayerConn();
Intent intent = new Intent(this, PlayService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
bar.setOnSeekBarChangeListener(this);
btPlay.setOnClickListener(this);
btPause.setOnClickListener(this);
btStop.setOnClickListener(this);
parseLrc();
}
private void initViews() {
surfaceView = (LrcSurfaceView) findViewById(R.id.lrc);
btPlay = (Button) findViewById(R.id.bt_play);
btPause = (Button) findViewById(R.id.bt_pause);
btStop = (Button) findViewById(R.id.bt_stop);
bar = (SeekBar) findViewById(R.id.bar);
}
/**
* 歌词解析
*/
public void parseLrc() {
BufferedReader bufr = null;
try {
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
bufr = new BufferedReader(new InputStreamReader(new FileInputStream(
Environment.getExternalStorageDirectory().getAbsolutePath()+ "/music.lrc")));
String data = null;
//添加第一条以歌名开头的歌词
list.add(new LrcInfo(0, "music"));
// "[00:09.77]Why did the writer complain to the people behind him?"
Pattern pattern = Pattern.compile("\\[(\\d{2}):(\\d{2}.\\d{2,3})\\](.*)");
while ((data = bufr.readLine()) != null) {
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
int minute = Integer.parseInt(matcher.group(1));//分钟 00
float second = Float.parseFloat(matcher.group(2));//秒 09.77
list.add(new LrcInfo((int) ((minute * 60 + second) * 1000), matcher.group(3)));
}
}
//为surfaceView设定歌词
surfaceView.setList(list);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(bufr != null){
try {
bufr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public void onClick(View v) {
Intent intent = new Intent(this, PlayService.class);
if (btPlay == v) {
intent.setAction(PlayService.ACTION_PLAY);
} else if (btPause == v) {
intent.setAction(PlayService.ACTION_PAUSE);
} else if (btStop == v) {
intent.setAction(PlayService.ACTION_STOP);
}
startService(intent);
}
private class PlayerConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = (IMyBinder) service;
binder.setHandler(handler); //设定handler
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
System.out.println("msg what:" + msg.what + " arg1:" + msg.arg1);
switch (msg.what) {
case PlayService.WHAT_DURATION:
bar.setMax(msg.arg1);
break;
case PlayService.WHAT_CURRENT:
if (!isDrag) {
//更新进度条以及歌词
bar.setProgress(msg.arg1);
surfaceView.update(msg.arg1);
}
break;
}
}
};
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(fromUser){
surfaceView.update(bar.getProgress());//更新歌词
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
//移除所有未处理的消息
handler.removeMessages(PlayService.WHAT_CURRENT);
isDrag = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//拖拽完成,更新
Intent intent = new Intent(this, PlayService.class);
intent.setAction(PlayService.ACTION_PLAY);
intent.putExtra("progress", bar.getProgress());
startService(intent);
isDrag = false;
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除服务
unbindService(conn);
}
}
/**
* PlayService.java
* @author zimo2013
* @see http://blog.csdn.net/zimo2013
*
*/
public class PlayService extends Service {
public static final String ACTION_PLAY = "play";
public static final String ACTION_PAUSE = "pause";
public static final String ACTION_STOP = "stop";
public static final int WHAT_CURRENT = 1;
public static final int WHAT_DURATION = 2;
private MediaPlayer player;
private Handler handler;
private IMyBinder binder;
private int progress;
@Override
public void onCreate() {
super.onCreate();
binder = new MyBinder();
}
@Override
public IBinder onBind(Intent intent) {
return (IBinder) binder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent.getAction();
if (ACTION_PLAY.equals(action)) {
progress = intent.getIntExtra("progress", 0);
toPlay(progress);
} else if (ACTION_PAUSE.equals(action)) {
pause();
} else if (ACTION_STOP.equals(action)) {
stop();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
if(player != null){
player.release(); //是否资源
player = null; //置空,拷贝GC回收
}
}
/**
* 将要播放
* @param pos
*/
private void toPlay(int pos) {
checkMediaPlayer();
if (state == PlayerState.Initialized) {
state = PlayerState.Preparing;
player.prepareAsync();
} else {
play(pos);
}
}
/**
* 实际播放音乐
* @param pos
*/
private void play(int pos) {
if (state == PlayerState.Started || state == PlayerState.Prepared
|| state == PlayerState.Paused
|| state == PlayerState.PlaybackCompleted) {
if (state != PlayerState.Paused) {
player.seekTo(pos);
}
player.start();
state = PlayerState.Started;
new Thread() {
public void run() {
// updateUI();
while (state == PlayerState.Started) {
send(WHAT_CURRENT, player.getCurrentPosition());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
} else {
show("播放异常");
}
}
/**
* 暂停播放
*/
private void pause() {
if (state == PlayerState.Started) {
player.pause();
state = PlayerState.Paused;
} else if (state == PlayerState.Paused) {
show("播放已经暂停,现不需要再暂停!");
} else {
show("暂停异常");
}
}
/**
* 停止播放
*/
private void stop() {
if (state == PlayerState.Started || state == PlayerState.Prepared
|| state == PlayerState.Paused
|| state == PlayerState.PlaybackCompleted) {
player.release(); //是否资源
player = null; //置空,拷贝GC回收
state = PlayerState.End;
} else {
show("停止异常");
}
}
/**
* 发送消息,用于更新ui
* @param what
* @param value
*/
private void send(int what, int value) {
// 更新进度条
Message msg = handler.obtainMessage();
msg.what = what;
msg.arg1 = value;
handler.sendMessage(msg);
}
/**
* 检查MediaPlayer对象,如果不存在则应该创建,并完成相关初始化
*/
private void checkMediaPlayer() {
if (player == null) {
// 完成多媒体的初始化
player = new MediaPlayer();
state = PlayerState.Idle;
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
// 判断SDcard正常挂载
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
try {
player.setDataSource(Environment
.getExternalStorageDirectory().getAbsolutePath()
+ "/music.mp3");
state = PlayerState.Initialized;
//异步准备
player.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
state = PlayerState.Prepared;
// 设定进度条的max值
send(WHAT_DURATION, player.getDuration());
// 准备完成
play(progress);
}
});
//播放完成
player.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
state = PlayerState.PlaybackCompleted;
// 播放完成
play(0);
show("已经播放完成,现在正在重新播放");
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 自定义MyBinder
* @author Administrator
*
*/
private class MyBinder extends Binder implements IMyBinder {
@Override
public void setHandler(Handler handler) {
PlayService.this.handler = handler;
}
}
/**
* 弹出吐司
* @param info
*/
public void show(String info) {
Toast.makeText(getApplicationContext(), info, 0).show();
}
private PlayerState state = PlayerState.Idle;
/**
* 播放器的播放状态枚举
* @author Administrator
*
*/
private enum PlayerState {
Idle, Initialized, Preparing, Prepared, Started, Stopped, Paused, End, PlaybackCompleted,
}
}
/**
* LrcSurfaceView.java
* @author zimo2013
* @see http://blog.csdn.net/zimo2013
*
*/
public class LrcSurfaceView extends SurfaceView {
private SurfaceHolder holder;
private List<LrcInfo> list;
private boolean isCreated;
private int mWidth;
private int mHeight;
private int pos = 0;
private Paint paintNew;
private Paint paintOld;
private int current;
private ExecutorService thread;
private Runnable runnable;
public LrcSurfaceView(Context context) {
this(context, null, 0);
}
public LrcSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
this.getHolder();
}
public LrcSurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
holder = getHolder();
holder.addCallback(new LrcCallBack());
// 线程池
thread = Executors.newSingleThreadExecutor();
runnable = new LrcUpdateRunnable();
// 创建2种画笔对象
paintNew = new Paint();
paintNew.setTextSize(20);
paintNew.setTextAlign(Paint.Align.CENTER);
paintNew.setColor(Color.YELLOW);
paintOld = new Paint();
paintOld.setTextSize(15);
paintOld.setTextAlign(Paint.Align.CENTER);//居中显示
paintOld.setColor(Color.WHITE);
}
/**
* 为surfaceView 设定歌词
*
* @param list
*/
public void setList(List<LrcInfo> list) {
Collections.sort(list); // 歌词排序,从前往后
this.list = list;
}
/**
* 更新歌词信息
*
* @param current
*/
public void update(int current) {
if (isCreated && list != null && list.size() > 0) {// 如果surfaceView对应已经被创建或者存在歌词
this.current = current;
thread.execute(runnable); // 线程池更新歌词信息
}
}
private class LrcUpdateRunnable implements Runnable {
@Override
public void run() {
pos = 0;// 默认从第一条歌词开始寻找
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.BLACK);
while (true) {
if (pos == list.size() - 1) {
break;
}
if (current >= list.get(pos).startTime && current < list.get(pos + 1).startTime) {
break;
}
pos++;
}//Surface可以直接操作UI
if (pos != 0) {// 上一条已经播放歌词
canvas.drawText(list.get(pos - 1).lrc, mWidth / 2, mHeight / 2 - 50, paintOld);
}
// 当前正在播放歌词
canvas.drawText(list.get(pos).lrc, mWidth / 2, mHeight / 2, paintNew);
// 下一条需要播放歌词
if (pos != list.size() - 1) {
canvas.drawText(list.get(pos + 1).lrc, mWidth / 2, mHeight / 2 + 50, paintOld);
}
// 接触锁定并提交
holder.unlockCanvasAndPost(canvas);
}
}
private class LrcCallBack implements SurfaceHolder.Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
isCreated = true;
mHeight = height;
mWidth = width;
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
}
/**
* IMyBinder.java
* @author zimo2013
* @see http://blog.csdn.net/zimo2013
*
*/
public interface IMyBinder {
void setHandler(Handler handler);
}