最近学习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;感觉用这个比较好,有利以后实现歌词同步。有空在研究实现歌词同步![]() |