最近学习android开发做了一个播放器练手,同样是新手可以看看交流交流,呵呵,有什么更好的实现方法忘能指教一下
图效果在附件
主activity audioList.java
package com.xianyifa.audioplayer; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.DisplayMetrics; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; import android.view.View; import android.view.View.OnCreateContextMenuListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.Toast; import com.xianyifa.audioplayer.impl.MyPlayer; import com.xianyifa.audioplayer.util.Toolbox; public class AudioList extends Activity { private final String TAG = "AudioList"; private String filepath;//音乐文件绝对路径 private String longClickFilePath;//长按的音乐文件绝对路径 private int position = 0;//当前播放位置 private MyPlayer myPlayerService;//播放服务对象 private MyServiceConnection conn; private Intent service;// 音乐播放服务意图 private String audioFile;// 音乐文件所在文件夹 private ListView listView; private List<HashMap<String, Object>> data;// listView的数据 private long audioLength;// 播放音乐长度 private SimpleAdapter adapter;// ListView适配器 private int listId = -1;// 当前播放的音乐在listView的索引 private int onListId = -1;// 上一首播放的音乐在listView的索引 private MediaPlayer mediaPlayer;// 服务的播放器 private Handler handler;// 用于主线程和子线程的通讯 private ControlPlayTime controlPlayTime;// 音乐时间更新控制线程 private boolean controlPlayStop = false;//控制音乐时间更新控制线程结束 private boolean isStop = false;// 标识播放器是否暂停 private boolean isPause = false;// 标识activity是否是在暂停恢复 private int widthPixels;//设备屏幕宽度像素 private final int ADDAUDIOPLAYER_ID = Menu.FIRST; private final int DELAUDIOPLAYER_ID = Menu.FIRST + 1; private final int EXITAUDIOPLAYER_ID = Menu.FIRST + 2; private final int DELETE_DIALOG = 1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.audiolist); service = new Intent(this, PlayerService.class);// 通过意图取得播放服务 // 驱动服务,1、,2、激活后回返回一个通道,Activity和service通讯是通过通道通讯的,服务通道是一个接口要实现 // 3、常量1自动创建 conn = new MyServiceConnection(); this.startService(service);// 先使用创建服务在绑定 this.bindService(service, conn, BIND_AUTO_CREATE); showListView(); //获取屏幕的宽带 DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); widthPixels = dm.widthPixels; Log.i(TAG, "onCreate"); } /* * 把数据绑定listview,并在界面显示 */ private void showListView(){ Log.i(TAG, "showListView"); // 读取文件夹的音乐列表 // 判断是否存在SD卡 File file; if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { audioFile = Environment.getExternalStorageDirectory() + "/myaudio"; file = new File(audioFile); // 判断目录是否存在 if (!file.exists()) { file.mkdirs(); } } else { audioFile = "/myaudio"; file = new File(audioFile); // 判断目录是否存在 if (!file.exists()) { file.mkdirs(); } } data = new ArrayList<HashMap<String, Object>>(); data = Toolbox.showCatalog(file); // 取得listview listView = (ListView) findViewById(R.id.audiolist); adapter = new MyAdapter(AudioList.this, data, R.layout.audiolistitem, new String[] { "filename", "filepath", "playTime", "audioTime" }, new int[] { R.id.audioname, R.id.audiopath, R.id.audioplaytime, R.id.audiotime }); listView.setAdapter(adapter); // 创建播放控制线程 controlPlayTime = new ControlPlayTime();// 取得播放时间控制线程 // 创建线程通讯监听 handler = new Handler() { @Override public void handleMessage(Message msg) { String message = (String) msg.obj; //如果message是数字,就是发过来的歌曲长度,不是则是转换后的播放时间点 if(message.matches("[0-9]+")){ //这是服务在播放,用户重新回到activity界面时更新UI显示当前播放歌曲信息 HashMap<String, Object> item = (HashMap<String, Object>) listView.getItemAtPosition(listId); item.put("audioTime", "/"+Toolbox.lengthTime(Long.parseLong(message))); }else{ if (onListId == listId) { HashMap<String, Object> item = (HashMap<String, Object>) listView.getItemAtPosition(listId); item.put("playTime", message); } else { // 当是服务换歌是在这里更新UI显示 HashMap<String, Object> playItem = (HashMap<String, Object>) listView.getItemAtPosition(listId); HashMap<String, Object> item = (HashMap<String, Object>) listView.getItemAtPosition(onListId); audioLength = myPlayerService.getPlayLength(); String time = Toolbox.lengthTime(audioLength); playItem.put("audioTime", "/" + time); playItem.put("playTime", "0:00"); item.put("audioTime", ""); item.put("playTime", ""); onListId = listId;//不要忘了这部要不然时间不会跳动 } } adapter.notifyDataSetChanged(); super.handleMessage(msg); } }; // 为listView注册单击事件 listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { ListView v = (ListView) parent; HashMap<String, Object> item = (HashMap<String, Object>) v.getItemAtPosition(position); // 上个播放的文件在ListView的位置 HashMap<String, Object> Befitem = null; if (listId > -1) { Befitem = (HashMap<String, Object>) v.getItemAtPosition(listId); } listId = position;// 保存listView索引 onListId = listId; filepath = item.get("filepath").toString();// 取得音乐路径 String playerFileName = myPlayerService.getFilePath(); if (mediaPlayer.isPlaying()) {// 如果正在播放 if (playerFileName.equals(filepath)) {// 而且请求的路径和现在播放的路径一样 myPlayerService.pause();// 暂停它 isStop = true; } else { try { myPlayerService.play(filepath, AudioList.this.position,listId); audioLength = mediaPlayer.getDuration(); String time = Toolbox.lengthTime(audioLength); item.put("audioTime", "/" + time); item.put("playTime", "0:00"); if (!Befitem.isEmpty() || Befitem != null) {// 吧上个播放的文件总时间删除 Befitem.put("audioTime", ""); Befitem.put("playTime", ""); } adapter.notifyDataSetChanged();// 让ListView更新 } catch (IOException e) { Log.i(TAG, e.toString()); } } } else {// 如果不是在播放 if (isStop && playerFileName.equals(filepath)) {// 判断是不是停止状态并且请求播放的是同一个文件 myPlayerService.pause(); } else {// 不是暂停状态的调用播放,或者是暂停但是请求的不是同一个音乐文件 try { myPlayerService.play(filepath, AudioList.this.position,listId); audioLength = mediaPlayer.getDuration(); String time = Toolbox.lengthTime(audioLength); // 判断线程是否活动状态 if (!controlPlayTime.isAlive()) { controlPlayTime.start();// 第一次执行播放开始线程 } item.put("audioTime", "/" + time); item.put("playTime", "0:00"); if (Befitem != null) {// 把上个播放的文件总时间删除,只有第一次播放和暂停换歌才会在这里掉用播放,只有暂停换歌才清空时间 Befitem.put("audioTime", ""); Befitem.put("playTime", ""); } adapter.notifyDataSetChanged();// 让ListView更新 } catch (IOException e) { Log.i(TAG, e.toString()); } } } } }); //为listview创建上文菜单 listView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { //设置图标 menu.setHeaderIcon(R.drawable.content_menu_ico); //设置标题 menu.setHeaderTitle(R.string.choice_action); //设置菜单 //播放 menu.add(R.string.player).setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { // TODO Auto-generated method stub return true; } }); //删除 menu.add(R.string.delete).setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { showDialog(DELETE_DIALOG);//显示提示框 return true; } }); } }); //为ListView创建一个item长按监听 listView.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { ListView v = (ListView)parent; HashMap<String, Object> item = (HashMap<String, Object>)v.getItemAtPosition(position); longClickFilePath = item.get("filepath").toString(); listView.showContextMenu(); return true; } }); } private class MyAdapter extends SimpleAdapter{ public MyAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) { super(context, data, resource, from, to); // TODO Auto-generated constructor stub } /* * 每次加载listView都会调用 * (non-Javadoc) * @see android.widget.SimpleAdapter#getView(int, android.view.View, android.view.ViewGroup) */ @Override public View getView(int position, View convertView, ViewGroup parent) { convertView = LayoutInflater.from(AudioList.this.getApplicationContext()).inflate(R.layout.audiolistitem, null); TextView fileNameText = (TextView)convertView.findViewById(R.id.audioname); TextView filePathText = (TextView)convertView.findViewById(R.id.audiopath); TextView playTimeText = (TextView)convertView.findViewById(R.id.audioplaytime); TextView fileTimeText = (TextView)convertView.findViewById(R.id.audiotime); TextView progressBarText = (TextView)convertView.findViewById(R.id.progress_bar); //比重新给值将显示空白?位置原因:getView就是把每天数据绑定到界面的过程,所以在这里要赋值 fileNameText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("filename").toString()); filePathText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("filepath").toString()); playTimeText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("playTime").toString()); fileTimeText.setText(((HashMap<String, Object>)listView.getItemAtPosition(position)).get("audioTime").toString()); if(listId == position){ //他要求传int 但不能传颜色的十进制代码 fileNameText.setTextColor(Color.parseColor("#3197FF")); fileNameText.setTextColor(Color.parseColor("#3197FF")); playTimeText.setTextColor(Color.parseColor("#3197FF")); fileTimeText.setTextColor(Color.parseColor("#3197FF")); //修改进度条长度 LayoutParams laParaContent = (LayoutParams)progressBarText .getLayoutParams(); laParaContent.width = getprogressBarSize(); progressBarText.setLayoutParams(laParaContent); // progressBarText.setWidth(0);//用这个更改不了 }else{ fileNameText.setTextColor(Color.parseColor("#000000")); } return convertView; } } /* * 计算进度条的尺寸 */ private int getprogressBarSize(){ double proportion = (double)mediaPlayer.getCurrentPosition()/(double)myPlayerService.getPlayLength(); int px = (int)(widthPixels * proportion); return px; } /* * 因为系统内存不足被摧毁 (non-Javadoc) * * @see android.app.Activity#onRestoreInstanceState(android.os.Bundle) */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { this.position = savedInstanceState.getInt("position"); this.filepath = savedInstanceState.getString("filepath"); Log.i(TAG, "onRestoreInstanceState"); super.onRestoreInstanceState(savedInstanceState); } /* * 因为系统内存不足被摧毁 (non-Javadoc) * * @see android.app.Activity#onSaveInstanceState(android.os.Bundle) */ @Override protected void onSaveInstanceState(Bundle outState) { outState.putInt("position", myPlayerService.getPosition()); outState.putString("filepath", myPlayerService.getFilePath()); Log.i(TAG, "onSaveInstanceState"); super.onSaveInstanceState(outState); } /* * 暂停了Activity (non-Javadoc) * * @see android.app.Activity#onPause() */ @Override protected void onPause() { if (myPlayerService != null) { myPlayerService.showNotification(); isPause = true; } Log.i(TAG, "onPause"); super.onPause(); } /* * 重新唤起,或刚开启都会调用 (non-Javadoc) * * @see android.app.Activity#onResume() */ @Override protected void onResume() { if (isPause && (myPlayerService != null)) { myPlayerService.hideNotification(); isPause = false; } Log.i(TAG, "onResume"); super.onResume(); } /* * 添加菜单 (non-Javadoc) * * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) */ @Override public boolean onCreateOptionsMenu(Menu menu) { // TODO Auto-generated method stub super.onCreateOptionsMenu(menu); // 退出程序 menu.add(0, ADDAUDIOPLAYER_ID, 0, "添加歌曲").setShortcut('2', 'b'); menu.add(0, DELAUDIOPLAYER_ID, 0, "删除歌曲").setShortcut('2', 'b'); // .setIcon(R.drawable.exit); // 退出程序 menu.add(0, EXITAUDIOPLAYER_ID, 0, "退出").setShortcut('4', 'd') .setIcon(R.drawable.exit); return true; } /* * 处理菜单动作 (non-Javadoc) * * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) */ @Override public boolean onOptionsItemSelected(MenuItem item) { // TODO Auto-generated method stub switch (item.getItemId()) { case EXITAUDIOPLAYER_ID: // 退出播放器 this.finish();//这里会执行解除绑定 controlPlayStop = true;//控制播放线程也结束 //等待播放控制线程结束才停止播放服务 while(controlPlayTime.isAlive()){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block Log.e(TAG, e.toString()); } Log.i(TAG, "wait controlPlayTime stop"); } this.stopService(service); return true; } return super.onOptionsItemSelected(item); } /* * 实现通道 */ private final class MyServiceConnection implements ServiceConnection { /* * 链接服务调用方法 service 为binder 通讯的桥梁 (non-Javadoc) * * @see * android.content.ServiceConnection#onServiceConnected(android.content * .ComponentName, android.os.IBinder) */ @Override public void onServiceConnected(ComponentName name, IBinder service) { // Binder binder = (Binder)service; myPlayerService = (MyPlayer) service; myPlayerService.setListViewData(data); listId = myPlayerService.getListId(); onListId = listId;//当前播放歌曲的listView数据索引 mediaPlayer = myPlayerService.getMediaPlayer();// 取得服务中的播放器 // 判断线程是否活动状态,并且音乐服务在播放 在这里就启动更新时间线程 if (myPlayerService.getIsPlayInit()) { audioLength = myPlayerService.getPlayLength();//当前播放歌曲的长度 controlPlayTime.start();// 第一次执行播放开始线程 } Log.i(TAG, "onServiceConnected"); } /* * 断开服务调用方法 (non-Javadoc) * * @see * android.content.ServiceConnection#onServiceDisconnected(android.content * .ComponentName) */ @Override public void onServiceDisconnected(ComponentName name) { myPlayerService = null; Log.i(TAG, "onServiceDisconnected"); } } /* * 播放时间更新控制线程,只有播放器存在才会启动 */ public class ControlPlayTime extends Thread { @Override public void run() { //线程刚启动就发给handler让他更新UI播放的音乐总长度 Message message1 = Message.obtain(); message1.obj = audioLength+"";//audioLength是long行要转换字符串传递 handler.sendMessage(message1); // 判断歌曲是否还在播放 while (!controlPlayStop) { long milliSecond = mediaPlayer.getCurrentPosition(); String time = Toolbox.formatTime(milliSecond); Message message = Message.obtain();// 用于和住线程通讯的消息容器,每次通讯都要创建 message.obj = time; handler.sendMessage(message); if((audioLength - milliSecond <= 1100)){ Log.i(TAG, "waiting next song"); while(audioLength == myPlayerService.getPlayLength()){ milliSecond = mediaPlayer.getCurrentPosition(); time = Toolbox.formatTime(milliSecond); Message message2 = Message.obtain();// 用于和住线程通讯的消息容器,每次通讯都要创建 message2.obj = time; handler.sendMessage(message2); Log.i(TAG, "ControlPlayTime waiting next song"); try { Thread.sleep(50); } catch (InterruptedException e) { Log.i(TAG, e.toString()); } } Log.i(TAG, "ControlPlayTime star updata next song playtime"); listId = myPlayerService.getListId(); //立即向handler发一个消息,这个消息内容没什么意义,只是使handler为下一首歌曲界面显示初始化 Message message3 = Message.obtain();// 用于和住线程通讯的消息容器,每次通讯都要创建 message3.obj = "0:00"; handler.sendMessage(message3); } try { Thread.sleep(1000); } catch (InterruptedException e) { Log.e(TAG, e.toString()); } } } } /* * 创建弹出确认窗口 * (non-Javadoc) * @see android.app.Activity#onCreateDialog(int, android.os.Bundle) */ @Override protected Dialog onCreateDialog(int id) { switch (id) { case DELETE_DIALOG: return new AlertDialog.Builder(AudioList.this) .setTitle(R.string.prompt).setMessage(getString(R.string.verify_del) +"\""+ longClickFilePath.substring(longClickFilePath.lastIndexOf("/")+1, longClickFilePath.length())+"\"?") .setPositiveButton(R.string.verify, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //确定执行操作 removeDialog(DELETE_DIALOG);//吧创建的弹出删除,不删除下次创建还是同一个对象,导致消息内容不变 } }).setNegativeButton(R.string.cancel, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { removeDialog(DELETE_DIALOG); } }).create(); default: return null; } } @Override protected void onDestroy() { controlPlayStop = true;//控制播放线程也结束 //等待播放控制线程结束才停止播放服务 while(controlPlayTime.isAlive()){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block Log.e(TAG, e.toString()); } Log.i(TAG, "wait controlPlayTmie stop"); } unbindService(conn); Log.i(TAG, "Activity onDestroy"); super.onDestroy(); } }
控制播放的服务PlayerService.java
package com.xianyifa.audioplayer; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.List; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.media.MediaPlayer; import android.os.Binder; import android.os.IBinder; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; import com.xianyifa.audioplayer.impl.MyPlayer; public class PlayerService extends Service { private final String TAG = "PlayerService"; private MediaPlayer mediaPlayer;//实例化一个播放器; private String filepath = null;//文件绝对路径 private int position;//播放的进度 private long playLength;//正在播放音乐的长度 private boolean isStop = false; private boolean controlPlayStop = false;//播放否控制 private boolean isPlayInit = false;//播放器是否初始化 private List<HashMap<String, Object>> listViewData;// listView的数据 private ControlPlay controlPlay;//播放控制线程 private int listId = -1; private boolean isShowNotification = false; private Binder binder = new MyBinder();//创建一个通讯,用于返回给调用,建立通讯桥梁,通讯都基于次桥梁 @Override public IBinder onBind(Intent arg0) { // 取得电话服务,实现电话进来的时候停止播放,挂断的继续 TelephonyManager telManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); // 注册监听,监听电话状态,并指定触发后执行的类,1、调用监听处理类方法,2、监听到的通讯状态(电话进入,接通电话,挂断电话) telManager.listen(new TelListenr(), PhoneStateListener.LISTEN_CALL_STATE); mediaPlayer = new MediaPlayer();//实例化一个播放器; controlPlay = new ControlPlay(); return binder; } public void setFilePath(String filepath){ this.filepath = filepath; } public void setPosition(int position){ this.position = position; } /* * 为Binder添加业务方法,只有在这里面才能通过Binder建立的通道进行调用 */ private final class MyBinder extends Binder implements MyPlayer{ public void setFilePath(String filepath){ PlayerService.this.setFilePath(filepath); } public void setListViewData(List<HashMap<String, Object>> listViewData){ PlayerService.this.listViewData = listViewData; } public void play(String filepath,int position,int id) throws IOException{ setFilePath(filepath);//在服务保存当前MP3路径 setPosition(position);//在服务保存当前MP3路径 listId = id; PlayerService.this.isStop = false; File file = new File(filepath); mediaPlayer.reset();//把之前的设置都重置一下 mediaPlayer.setDataSource(file.getAbsolutePath());//设置音乐文件路径 mediaPlayer.prepare();//缓存一定要调用,初始化 mediaPlayer.start(); mediaPlayer.seekTo(position); isPlayInit = true; playLength = mediaPlayer.getDuration(); if(!PlayerService.this.controlPlay.isAlive()){ controlPlay.start(); } } public boolean pause(){ if(mediaPlayer.isPlaying()){//如果是在播放 mediaPlayer.pause(); return true; }else{ //应为如果按了停止直接使用start 继续叫报错 if(!PlayerService.this.isStop){ mediaPlayer.start(); } return false; } } public void reset()throws IOException{ if(mediaPlayer.isPlaying()){ mediaPlayer.seekTo(0); }else if(PlayerService.this.filepath != null){//确保用户先点击过播放 play(PlayerService.this.filepath,0,listId); } } public void stop(){ if(mediaPlayer.isPlaying()){ mediaPlayer.stop(); PlayerService.this.isStop = true; } } public int getPosition(){ return mediaPlayer.getCurrentPosition(); } public String getFilePath(){ return PlayerService.this.filepath; } public MediaPlayer getMediaPlayer(){ return mediaPlayer; } public int getListId(){ return listId; } public boolean getIsPlayInit(){ return isPlayInit; } public long getPlayLength(){ return playLength; } /* * 显示通知栏图标,当界面不可见的时候调用 * (non-Javadoc) * @see com.xianyifa.audioplayer.impl.MyPlayer#showNotification() */ public void showNotification(){ // 创建一个NotificationManager的引用 NotificationManager notificationManager = (NotificationManager) PlayerService.this.getSystemService(android.content.Context.NOTIFICATION_SERVICE); // 定义Notification的各种属性 Notification notification =new Notification(R.drawable.icon, (filepath != null) ? filepath.substring(filepath.lastIndexOf("/")+1, filepath.length()) : "无音乐播放", System.currentTimeMillis()); notification.flags |= Notification.FLAG_ONGOING_EVENT; // 将此通知放到通知栏的"Ongoing"即"正在运行"组中 notification.flags |= Notification.FLAG_NO_CLEAR; // 表明在点击了通知栏中的"清除通知"后,此通知不清除,经常与FLAG_ONGOING_EVENT一起使用 notification.flags |= Notification.FLAG_SHOW_LIGHTS; notification.defaults = Notification.DEFAULT_LIGHTS; notification.ledARGB = Color.BLUE; notification.ledOnMS =5000; // 设置通知的事件消息 CharSequence contentTitle = "正在播放……"; // 通知栏标题 CharSequence contentText = (filepath != null) ? filepath.substring(filepath.lastIndexOf("/")+1, filepath.length()) :"无音乐播放"; // 通知栏内容 // CharSequence contentText = "无音乐播放"; // 通知栏内容 Intent notificationIntent = new Intent(PlayerService.this, AudioList.class); // 点击该通知后要跳转的Activity //添加这里可以解决当按home键停止activity在冲通知进入时出现多个activity对象 //也就是再按返回是跳到另一个还是这个界面的activity notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent contentItent = PendingIntent.getActivity(PlayerService.this, 0, notificationIntent, 0); notification.setLatestEventInfo(PlayerService.this, contentTitle, contentText, contentItent); // 把Notification传递给NotificationManager notificationManager.notify(0, notification); isShowNotification = true; } /* * 删除通知栏的图标 * (non-Javadoc) * @see com.xianyifa.audioplayer.impl.MyPlayer#hideNotification() */ public void hideNotification(){ // 启动后删除之前我们定义的通知 NotificationManager notificationManager = (NotificationManager) PlayerService.this .getSystemService(NOTIFICATION_SERVICE); notificationManager.cancel(0); isShowNotification = false; } } /* * 实现电话状态类 */ private class TelListenr extends PhoneStateListener { @Override public void onCallStateChanged(int state, String incomingNumber) { try { switch (state) { case TelephonyManager.CALL_STATE_IDLE:// 挂断 PlayerService.this.mediaPlayer.start(); break; case TelephonyManager.CALL_STATE_OFFHOOK:// 接通电话 break; case TelephonyManager.CALL_STATE_RINGING:// 电话进入 if(PlayerService.this.mediaPlayer.isPlaying()){//如果是在播放 PlayerService.this.mediaPlayer.pause();//暂停 } break; default: break; } } catch (Exception e) { // TODO Auto-generated catch block Log.i(TAG, e.toString()); } super.onCallStateChanged(state, incomingNumber); } } /* * 播放控制线程 */ public class ControlPlay extends Thread { @Override public void run() { while (!controlPlayStop) { try { Thread.sleep(800); } catch (InterruptedException e) { Log.e(TAG, e.toString()); } long milliSecond = mediaPlayer.getCurrentPosition(); if((playLength - milliSecond) <= 1100){ // String filepath = filename; String audioName = filepath.substring(filepath.lastIndexOf("/")+1, filepath.length()); //第一次换歌没有初始化,先找到当前在listView的索引以后往上加就可以知道列表末尾 if(listId == -1){ listId = getListId(audioName, filepath); } if(listId < (listViewData.size() - 1)){ listId += 1; }else{ listId = 0; } String playPath = getFilePath(listId); Log.i(TAG, listId+"------"+playPath+"----"+listViewData.size()); MyPlayer myPlayer = (MyBinder)binder; try { Log.i(TAG, "service waiting 3 Second play next song"); Thread.sleep(3000); myPlayer.play(playPath, 0,listId); } catch (Exception e) { Log.e(TAG, e.toString()); } //判断通知是不是显示,做出对通知信息的更改 if(isShowNotification){ myPlayer.hideNotification(); myPlayer.showNotification(); } } } } } /* * data list绑定的数据 * filepath 正在播放歌曲的路径 * filename 正在播放歌曲名称 * return i 返回正在播放的歌曲在listView绑定数据的索引 */ private int getListId(String filename,String filepath){ int i = 0; for(HashMap<String,Object> audio : listViewData){ if(audio.get("filepath").equals(filepath)){ break; } i++; } return i; } /* * index 歌曲在listView绑定数据的索引 * return 返回歌曲的绝对路径 */ private String getFilePath(int index){ HashMap<String,Object> audio = listViewData.get(index); return audio.get("filepath").toString(); } @Override public void onDestroy() { controlPlayStop = true;//控制播放线程也结束 //等待播放控制线程结束才停止播放服务 while(controlPlay.isAlive()){ try { Thread.sleep(1000); } catch (InterruptedException e) { Log.e(TAG, e.toString()); } Log.i(TAG, "wait controlPlay stop"); } //播放控制线程停止后在停止播放器 if(mediaPlayer != null){ MyPlayer myPlayer = (MyBinder)binder; myPlayer.hideNotification();//服务结束的时候一定要清楚通知栏 mediaPlayer.stop();//一定要在这里停止,要不然服务停止了播放器还是会继续播放 mediaPlayer.release();//释放资源 } super.onDestroy(); } }
实现服务和activity通讯的binder继承类的接口
package com.xianyifa.audioplayer.impl; import java.io.IOException; import java.util.HashMap; import java.util.List; import android.media.MediaPlayer; import android.os.Handler; public interface MyPlayer { public void play(String filename,int position,int id) throws IOException; public boolean pause(); public void reset() throws IOException; public void stop(); public void setFilePath(String filepath); public void showNotification(); public void hideNotification(); public int getPosition(); public String getFilePath(); public MediaPlayer getMediaPlayer(); public void setListViewData(List<HashMap<String, Object>> listViewData); public int getListId(); public boolean getIsPlayInit(); public long getPlayLength(); }
主界面XMLaudiolist.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@android:color/white" > <ListView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/audiolist" android:cacheColorHint="#00000000" > </ListView> </LinearLayout>
item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:id="@+id/listviewitem" > <LinearLayout android:layout_width="fill_parent" android:layout_height="40dip" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/audioname" android:layout_marginLeft="20dip" android:layout_marginTop="10dip" android:textColor="@android:color/black" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" android:layout_marginTop="10dip" android:layout_marginRight="20dip" android:gravity="right"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/audioplaytime" android:layout_gravity="right" android:textColor="@android:color/black" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/audiotime" android:textColor="@android:color/black" /> </LinearLayout> <TextView android:layout_width="0dip" android:layout_height="0dip" android:id="@+id/audiopath" /> </LinearLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="1dip" android:orientation="horizontal" > <TextView android:layout_width="0px" android:layout_height="fill_parent" android:id="@+id/progress_bar" android:background="@android:color/black" /> </LinearLayout> </LinearLayout> 工具类 Toolbox .java package com.xianyifa.audioplayer.util; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class Toolbox { // 调用文件目录查询方法 //方法一;以绝对路径输出给定的目录下的所有文件路径 public static List<HashMap<String, Object>> showCatalog(File file) { //System.out.println(file.getName()); List<HashMap<String, Object>> fileList = new ArrayList<HashMap<String, Object>>(); File[] files = file.listFiles(); if (files != null) { for (File f : files) { if (f.isDirectory()) {//判断是否是目录 showCatalog(f); } else { // System.out.println(f.getName());//输出文件或文件夹名称 if(f.getName().substring(f.getName().lastIndexOf("."), f.getName().length()).equals(".mp3")){ HashMap<String, Object> item = new HashMap<String, Object>(); item.put("filepath",f.getAbsolutePath());//输出绝对路径 item.put("filename",f.getName());//输出绝对路径 item.put("playTime","");//输出绝对路径 item.put("audioTime","");//输出绝对路径 fileList.add(item); } } } } return fileList; } /* * 格式化当前播放时间点时间 */ public static String formatTime(long milliSecond){ int minute = (int)(milliSecond/1000)/60; int second = (int)(milliSecond/1000)%60; String sec = second+""; if(second<10){ sec = "0"+second; } String time = minute+":"+sec; return time; } /* * 求总时长 */ public static String lengthTime(long milliSecond){ int minute = (int)(milliSecond/1000)/60; int second = (int)(milliSecond/1000)%60; if(milliSecond%1000 > 500){ second += 1; if(second == 60){ minute += 1; second = 0; } } String sec = second+""; if(second<10){ sec = "0"+second; } String time = minute+":"+sec; return time; } }
新上传的源码修改了一些小地方,服务播放变更改用广播通知activity;感觉用这个比较好,有利以后实现歌词同步。有空在研究实现歌词同步 |