MainActivity 代码
package cn.tedu.music_player_2.activity;
import java.util.List;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import cn.tedu.music_player_2.R;
import cn.tedu.music_player_2.adapter.MusicAdapter;
import cn.tedu.music_player_2.app.MusicPlayerApplication;
import cn.tedu.music_player_2.entity.Music;
import cn.tedu.music_player_2.service.PlayMusicService;
import cn.tedu.music_player_2.util.CommonUtils;
import cn.tedu.music_player_2.util.IMusicPlayer;
/**
* MainActivity
*
* @author chengheng
* @version 2.0
*/
public class MainActivity extends Activity implements View.OnClickListener,
OnItemClickListener, OnSeekBarChangeListener {
/**
* ListView:歌曲列表
*/
private ListView lvMusics;
/**
* ImageButton:播放或暂停
*/
private ImageButton ibPlayOrPause;
/**
* ImageButton:播放上一首
*/
private ImageButton ibPrevious;
/**
* ImageButton:播放下一首
*/
private ImageButton ibNext;
/**
* TextView:显示当前播放的歌曲的名称
*/
private TextView tvMusicTitle;
/**
* SeekBar:歌曲的播放进度
*/
private SeekBar sbMusicProgress;
/**
* TextView:歌曲当前播放到的时间
*/
private TextView tvMusicCurrentPosition;
/**
* TextView:当前播放的歌曲的时长
*/
private TextView tvMusicDuration;
/**
* 歌曲数据的List集合
*/
private List musics;
/**
* 歌曲列表的Adapter
*/
private MusicAdapter musicAdapter;
/**
* Application
*/
private MusicPlayerApplication app;
/**
* ServiceConnection:Activity与Service建立的连接
*/
private InnerServiceConnection conn;
/**
* 当Activity与Service连接后,调用Service方法的工具
*/
private IMusicPlayer player;
/**
* 更新歌曲播放信息的线程
*/
private UpdateProgressThread updateProgressThread;
/**
* 播放器是否已经开始运行
*/
private boolean isPlayerWorking;
/**
* 用户是否正在拖拽进度条
*/
private boolean isUserTrackingTouch;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定Service
Intent intent = new Intent(this, PlayMusicService.class);
conn = new InnerServiceConnection();
bindService(intent, conn, BIND_AUTO_CREATE);
// 初始化控件
lvMusics = (ListView) findViewById(R.id.lv_musics);
ibPlayOrPause = (ImageButton) findViewById
(R.id.ib_play_or_pause);
ibPrevious = (ImageButton) findViewById(R.id.ib_previous);
ibNext = (ImageButton) findViewById(R.id.ib_next);
tvMusicTitle = (TextView) findViewById(R.id.tv_music_title);
sbMusicProgress = (SeekBar) findViewById(R.id.sb_music_progress);
tvMusicCurrentPosition = (TextView) findViewById
(R.id.tv_music_current_position);
tvMusicDuration = (TextView) findViewById
(R.id.tv_music_duration);
// 获取歌曲列表的数据
app = (MusicPlayerApplication) getApplication();
musics = app.getMusics();
// 显示歌曲列表的ListView
musicAdapter = new MusicAdapter(this, musics);
lvMusics.setAdapter(musicAdapter);
// 为按钮配置监听器
ibPlayOrPause.setOnClickListener(this);
ibPrevious.setOnClickListener(this);
ibNext.setOnClickListener(this);
// 为ListView配置监听器
lvMusics.setOnItemClickListener(this);
// 为SeekBar配置监听器
sbMusicProgress.setOnSeekBarChangeListener(this);
}
@Override
protected void onRestart() {
// 判断是否正在播放
if(player.callIsPlaying()) {
// 开启线程更新播放信息
startUpdateProgressThread();
}
super.onRestart();
}
@Override
protected void onStop() {
// 停止更新播放信息
stopUpdateProgressThread();
super.onStop();
}
@Override
protected void onDestroy() {
// 解除与Service的绑定
if (conn != null) {
unbindService(conn);
}
super.onDestroy();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ib_play_or_pause:
// 判断当前的状态
if (player.callIsPlaying()) {
// 正在播放,则暂停
player.callPause();
// 更换按钮图片:换为“播放”
ibPlayOrPause.setImageResource
(android.R.drawable.ic_media_play);
// 停止更新歌曲播放信息
stopUpdateProgressThread();
} else {
// 没有播放,则播放
player.callPlay();
// 更换按钮图片:换为“暂停”
ibPlayOrPause.setImageResource
(android.R.drawable.ic_media_pause);
// 开启线程,更新播放信息
startUpdateProgressThread();
// 更新标识:播放器开始工作
isPlayerWorking = true;
}
break;
case R.id.ib_previous:
// 播放上一首
player.callPrevious();
// 更换按钮图片:换为“暂停”
ibPlayOrPause.setImageResource
(android.R.drawable.ic_media_pause);
// 开启线程,更新播放信息
startUpdateProgressThread();
// 更新标识:播放器开始工作
isPlayerWorking = true;
break;
case R.id.ib_next:
// 播放下一首
player.callNext();
// 更换按钮图片:换为“暂停”
ibPlayOrPause.setImageResource
(android.R.drawable.ic_media_pause);
// 开启线程,更新播放信息
startUpdateProgressThread();
// 更新标识:播放器开始工作
isPlayerWorking = true;
break;
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// 播放指定的歌曲
player.callPlay(position);
// 更换按钮图片:换为“暂停”
ibPlayOrPause.setImageResource
(android.R.drawable.ic_media_pause);
// 开启线程,更新播放信息
startUpdateProgressThread();
// 更新标识:播放器开始工作
isPlayerWorking = true;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 标志用户正在拖拽进度条
isUserTrackingTouch = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 标志用户已经停止拖拽进度条
isUserTrackingTouch = false;
// 判断播放器是否已开始工作
if(isPlayerWorking) {
// 获取拖拽后的进度
int progress = sbMusicProgress.getProgress();
// 调用Service中对应的方法
player.callSeekTo(progress);
// 开启线程,更新播放进度
startUpdateProgressThread();
// 设置播放按钮显示的图片为“暂停”
ibPlayOrPause.setImageResource
(android.R.drawable.ic_media_pause);
}
}
/**
* 开启线程,更新播放信息
*/
private void startUpdateProgressThread() {
if(updateProgressThread == null) {
updateProgressThread = new UpdateProgressThread();
updateProgressThread.setRunning(true);
updateProgressThread.start();
}
}
/**
* 停止更新播放信息的线程
*/
private void stopUpdateProgressThread() {
if(updateProgressThread != null) {
updateProgressThread.setRunning(false);
updateProgressThread = null;
}
}
/**
* 更新播放信息的线程
*/
private class UpdateProgressThread extends Thread {
private boolean isRunning;
private String musicName;
private int progress;
private String currentPositionString;
private String durationString;
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
private Runnable runner = new Runnable() {
@Override
public void run() {
// 更新控件:歌曲标题
tvMusicTitle.setText(musicName);
// 更新控件:当前歌曲时长
tvMusicDuration.setText(durationString);
// 更新控件:当前播放时间
tvMusicCurrentPosition.setText
(currentPositionString);
// 判断用户是否正在拖拽进度条
if(!isUserTrackingTouch) {
// 更新控件:进度条
sbMusicProgress.setProgress(progress);
}
}
};
@Override
public void run() {
// 循环
while(isRunning) {
// 日志
Log.d("tedu", "UpdateProgressThread.run()");
// 判断是否正在播放
if(player.callIsPlaying()) {
// 获取所需的数据
int currentMusicIndex =
player.callGetCurrentMusicIndex();
int currentPosition =
player.callGetCurrentPosition();
int duration = player.callGetDuration();
// 计算需要被显示的数据
musicName = "正在播放:" + musics.get
(currentMusicIndex).getName();
progress = currentPosition * 100 /
duration;
currentPositionString =
CommonUtils.getFormattedTime(currentPosition);
durationString =
CommonUtils.getFormattedTime(duration);
// 更新显示
runOnUiThread(runner);
}
// 休眠
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* ServiceConnection
*/
private class InnerServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder
service) {
player = (IMusicPlayer) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
// (无视)
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean
fromUser) {
// (无视)
}
}
MusicAdapter 代码
package cn.tedu.music_player_2.adapter;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import cn.tedu.music_player_2.R;
import cn.tedu.music_player_2.R.id;
import cn.tedu.music_player_2.R.layout;
import cn.tedu.music_player_2.entity.Music;
/**
* 显示歌曲列表ListView的Adapter
* @author chengheng
* @version 2.0
*/
public class MusicAdapter extends BaseAdapter {
/**
* Context
*/
private Context context;
/**
* 歌曲列表的数据
*/
private List musics;
/**
* 构造方法
* @param context 上下文对象
* @param musics 歌曲的数据的List集合
*/
public MusicAdapter(Context context, List<Music> musics) {
super();
this.context = context;
this.musics = musics;
}
@Override
public int getCount() {
return musics.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Music music = musics.get(position);
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.music_item, null);
}
TextView tvName = (TextView) convertView.findViewById(R.id.tv_music_item_name);
TextView tvPath = (TextView) convertView.findViewById(R.id.tv_music_item_path);
tvName.setText(music.getName());
tvPath.setText(music.getPath());
return convertView;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
}
自定义的application代码
package cn.tedu.music_player_2.app;
import java.util.List;
import android.app.Application;
import android.util.Log;
import cn.tedu.music_player_2.dal.IDao;
import cn.tedu.music_player_2.dal.MusicDaoFactory;
import cn.tedu.music_player_2.entity.Music;
public class MusicPlayerApplication extends Application {
private List musics = null;
@Override
public void onCreate() {
// 日志
Log.e("tedu", "MusicPlayerApplication.onCreate()");
// 获取数据
Log.e("tedu", "MusicPlayerApplication. 准备获取歌曲列表!");
// MusicDao dao = new MusicDao();
IDao dao = MusicDaoFactory.newInstance();
musics = dao.getData();
Log.e("tedu", "MusicPlayerApplication. 获取歌曲列表完成!");
}
/**
* 获取歌曲数据的List集合
* @return 歌曲数据的List集合,该返回值永不为null
*/
public List<Music> getMusics() {
return musics;
}
}
工具类IDao代码:
package cn.tedu.music_player_2.dal;
import java.util.List;
import cn.tedu.music_player_2.entity.Music;
public interface IDao {
List<Music> getData();
}
工具类InternetMusicDao 代码:
package cn.tedu.music_player_2.dal;
import java.util.ArrayList;
import java.util.List;
import cn.tedu.music_player_2.entity.Music;
public class InternetMusicDao implements IDao {
@Override
public List<Music> getData() {
List<Music> musics = new ArrayList<Music>();
Music m1 = new Music();
m1.setName("长城");
m1.setPath("http://mp3.baidu.com/changcheng.mp3");
musics.add(m1);
Music m2 = new Music();
m2.setName("大地");
m2.setPath("http://mp3.baidu.com/dadi.mp3");
musics.add(m2);
Music m3 = new Music();
m3.setName("光辉岁月");
m3.setPath("http://mp3.baidu.com/guanghuisuiyue.mp3");
musics.add(m3);
return musics;
}
}
LoadMusicDao 代码;
package cn.tedu.music_player_2.dal;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import android.os.Environment;
import android.util.Log;
import cn.tedu.music_player_2.entity.Music;
/**
* 歌曲数据的访问类
* @author chengheng
* @version 2.0
*/
public class LocalMusicDao implements IDao {
/**
* 扫描sdcard中Music下所有.mp3文件
* @return 扫描到的Music对象的List集合,该返回值永不为null
*/
@Override
public List getData() {
// 准备返回值
List musics = new ArrayList();
// 扫描sdcard中Music下所有.mp3文件
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
if (dir.exists()) {
File[] files = dir.listFiles();
if (files != null && files.length > 0) {
for (int i = 0; i < files.length; i++) {
if (files[i].isFile()) {
if (files[i].getName().toLowerCase(Locale.CHINA).endsWith(".mp3")) {
Music music = new Music();
music.setPath(files[i].getAbsolutePath());
music.setName(files[i].getName().substring(0, files[i].getName().length() - 4));
Log.v("tedu", "" + music);
musics.add(music);
}
}
}
} else {
Log.w("tedu", "Music文件夹下没有有效的文件!");
}
} else {
Log.w("tedu", "sdcard下Music文件夹不存在!无法扫描到歌曲!");
}
} else {
Log.w("tedu", "sdcard没有正确挂载!无法访问!");
}
// 返回
return musics;
}
}
工具类MusicDaoFactory 代码;
package cn.tedu.music_player_2.dal;
public class MusicDaoFactory {
private MusicDaoFactory() {
}
public static IDao newInstance() {
return new LocalMusicDao();
}
}
实体类 Music 代码
package cn.tedu.music_player_2.entity;
/**
* 歌曲信息的实体类
* @author chengheng
* @version 2.0
*/
public class Music {
/**
* 歌曲名称,用于显示
*/
private String name;
/**
* 歌曲路径
*/
private String path;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return "Music [name=" + name + ", path=" + path + "]";
}
}
service服务PlayMusicSevice
package cn.tedu.music_player_2.service;
import java.io.IOException;
import java.util.List;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import cn.tedu.music_player_2.app.MusicPlayerApplication;
import cn.tedu.music_player_2.entity.Music;
import cn.tedu.music_player_2.util.IMusicPlayer;
public class PlayMusicService extends Service {
/**
* 播放工具
*/
private MediaPlayer player;
/**
* 歌曲数据的List集合
*/
private List musics;
/**
* Application
*/
private MusicPlayerApplication app;
/**
* 当前播放的歌曲的索引
*/
private int currentMusicIndex;
/**
* 暂停位置
*/
private int pausePosition;
@Override
public void onCreate() {
// 创建MediaPlayer
player = new MediaPlayer();
// 获取歌曲列表的数据
app = (MusicPlayerApplication) getApplication();
musics = app.getMusics();
// 为MediaPlayer对象配置监听器
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// 播放下一首
next();
}
});
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// 快进到之前暂停的位置
player.seekTo(pausePosition);
// 播放
player.start();
}
});
}
@Override
public IBinder onBind(Intent intent) {
InnerBinder binder = new InnerBinder();
return binder;
}
@Override
public void onDestroy() {
// 释放播放器相关资源
player.release();
player = null;
}
/**
* Binder
*/
private class InnerBinder extends Binder implements IMusicPlayer {
@Override
public void callPlay() {
PlayMusicService.this.play();
}
@Override
public void callPause() {
PlayMusicService.this.pause();
}
@Override
public void callPrevious() {
PlayMusicService.this.previous();
}
@Override
public void callNext() {
PlayMusicService.this.next();
}
@Override
public boolean callIsPlaying() {
return player.isPlaying();
}
@Override
public void callPlay(int position) {
PlayMusicService.this.play(position);
}
@Override
public int callGetCurrentMusicIndex() {
return currentMusicIndex;
}
@Override
public int callGetCurrentPosition() {
return player.getCurrentPosition();
}
@Override
public int callGetDuration() {
return player.getDuration();
}
@Override
public void callSeekTo(int progress) {
PlayMusicService.this.seekTo(progress);
}
}
/**
* 播放
*/
private void play() {
try {
// 1. 重置
player.reset();
// 2. 设置歌曲的路径
player.setDataSource(musics.get(currentMusicIndex).getPath());
// 3. 异步加载歌曲数据,必须匹配OnPreparedListener
player.prepareAsync();
// // 3. 加载歌曲数据
// player.prepare();
// // 4. 快进到暂停位置
// player.seekTo(pausePosition);
// // 5. 播放
// player.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 播放指定的歌曲
*
* @param position
* 歌曲的索引
*/
private void play(int position) {
// 确定需要播放的歌曲的索引
currentMusicIndex = position;
// 清除暂停位置
pausePosition = 0;
// 播放
play();
}
/**
* 暂停
*/
private void pause() {
// 暂停播放
player.pause();
// 记录当前播放到的位置
pausePosition = player.getCurrentPosition();
}
/**
* 播放上一首
*/
private void previous() {
// 计算上一首歌曲的索引值
currentMusicIndex--;
if (currentMusicIndex < 0) {
currentMusicIndex = musics.size() - 1;
}
// 清除暂停位置
pausePosition = 0;
// 播放
play();
}
/**
* 播放下一首
*/
private void next() {
// 计算上一首歌曲的索引值
currentMusicIndex++;
if (currentMusicIndex >= musics.size()) {
currentMusicIndex = 0;
}
// 清除暂停位置
pausePosition = 0;
// 播放
play();
}
/**
* 快进到指定的进度位置开始播放
*
* @param progress
* 进度值,假设从60%位置开始播放,则参数值为60
*/
private void seekTo(int progress) {
// 根据进度计算出需要从??毫秒开始播放,并将值赋给pausePosition
pausePosition = player.getDuration() * progress / 100;
// 播放
play();
}
}
commonutils 类代码:
package cn.tedu.music_player_2.util;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Locale;
/**
* 通用工具类
*/
public class CommonUtils {
/**
* 格式化时间的工具
*/
private static SimpleDateFormat sdf = new SimpleDateFormat(“mm:ss”, Locale.CHINA);
/**
* 被格式化的时间对象
*/
private static Date date = new Date();
/**
* 获取格式化后的时间
*
* @param timeMillis
* 时间值,取值为从1970-1-1 0:00:00至今的毫秒数
* @return 格式化为mm:ss格式的字符串
*/
public static String getFormattedTime(long timeMillis) {
// 设置时间
date.setTime(timeMillis);
// 返回格式化后的结果
return sdf.format(date);
}
}
consts工具类
package cn.tedu.music_player_2.util;
/**
* 保存常量
* @author chengheng
* @version 2.0
*/
public interface Consts {
/**
* 指令:播放或暂停
*/
int COMMAND_PLAY_OR_PAUSE = 0;
/**
* 指令:播放上一首
*/
int COMMAND_PREVIOUS = 1;
/**
* 指令:播放下一首
*/
int COMMAND_NEXT = 2;
/**
* 指令:播放指定的歌曲
*/
int COMMAND_PLAY_POSITION = 3;
/**
* Intent中封装的数据的key:指令
*/
String EXTRA_COMMAND = "command";
/**
* Intent中封装的数据的key:需要播放的歌曲的索引
*/
String EXTRA_POSITION = "position";
}
IMusicPlayer类代码:
package cn.tedu.music_player_2.util;
public interface IMusicPlayer {
/**
* 播放歌曲
*/
void callPlay();
/**
* 暂停播放
*/
void callPause();
/**
* 播放上一首
*/
void callPrevious();
/**
* 播放下一首
*/
void callNext();
/**
* 获取播放状态,即判断是否正在播放
*
* @return 返回true则表示正在播放,返回false则表示没有播放
*/
boolean callIsPlaying();
/**
* 播放指定的歌曲
*
* @param position
* 歌曲的索引
*/
void callPlay(int position);
/**
* 获取当前播放的歌曲的索引
*
* @return 当前播放的歌曲的索引
*/
int callGetCurrentMusicIndex();
/**
* 获取当前播放到的位置
*
* @return 当前播放到的位置
*/
int callGetCurrentPosition();
/**
* 获取当前播放的歌曲的时长
*
* @return 当前播放的歌曲的时长
*/
int callGetDuration();
/**
* 快进到指定的进度位置开始播放
*
* @param progress
* 进度值,假设从60%位置开始播放,则参数值为60
*/
void callSeekTo(int progress);
}