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中Dialog的使用详解

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

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析

《MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析》本文将详细讲解MyBatis-Plus中的lambdaUpdate用法,并提供丰富的案例来帮助读者更好地理解和应... 目录深入探索MyBATis-Plus中Service接口的lambdaUpdate用法及示例案例背景

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Linux搭建Mysql主从同步的教程

《Linux搭建Mysql主从同步的教程》:本文主要介绍Linux搭建Mysql主从同步的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux搭建mysql主从同步1.启动mysql服务2.修改Mysql主库配置文件/etc/my.cnf3.重启主库my

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的