Android_播放器_利用Service通过MediaPlayer播放歌曲并完成歌词同步绘制

本文主要是介绍Android_播放器_利用Service通过MediaPlayer播放歌曲并完成歌词同步绘制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本博文为子墨原创,转载请注明出处!
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;@Overrideprotected 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.77list.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();}}}}@Overridepublic 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 {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {binder = (IMyBinder) service;binder.setHandler(handler);	//设定handler}@Overridepublic 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;}}};@Overridepublic void onProgressChanged(SeekBar seekBar, int progress,boolean fromUser) {if(fromUser){surfaceView.update(bar.getProgress());//更新歌词}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {//移除所有未处理的消息handler.removeMessages(PlayService.WHAT_CURRENT);isDrag = true;}@Overridepublic 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;}@Overrideprotected 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;@Overridepublic void onCreate() {super.onCreate();binder = new MyBinder();}@Overridepublic IBinder onBind(Intent intent) {return (IBinder) binder;}@Overridepublic 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);}@Overridepublic 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() {@Overridepublic void onPrepared(MediaPlayer mp) {state = PlayerState.Prepared;// 设定进度条的max值send(WHAT_DURATION, player.getDuration());// 准备完成play(progress);}});//播放完成player.setOnCompletionListener(new OnCompletionListener() {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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可以直接操作UIif (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 {@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {isCreated = true;mHeight = height;mWidth = width;}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}}}
/*** IMyBinder.java* @author zimo2013* @see http://blog.csdn.net/zimo2013**/
public interface IMyBinder {void setHandler(Handler handler);
}

这篇关于Android_播放器_利用Service通过MediaPlayer播放歌曲并完成歌词同步绘制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/685693

相关文章

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.

Android Studio 配置国内镜像源的实现步骤

《AndroidStudio配置国内镜像源的实现步骤》本文主要介绍了AndroidStudio配置国内镜像源的实现步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、修改 hosts,解决 SDK 下载失败的问题二、修改 gradle 地址,解决 gradle

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(