本音乐播放器的功能:
1.实现访问手机本地的.mp3音乐文件;
2.在主页中将访问的数据显示出来(这里简单点用ListView,用RecycleView当然也可以);
3.在播放音乐页面实现上一首、下一首、播放/暂停、进度条随音乐播放滑动、动态显示播放时间,拖动进度条,当前播放时间动态改变并且播放进度也会改变、自动切换下一首歌;
4.从在Activity中播放音乐过渡到在Service中播放音乐;
5.改进该项目,使用广播的方式播放音乐并能够在Notification中控制音乐的播放状态。
由于我将全部代码都贴在了本博客,所以文章篇幅较长。
我的最终效果图:
本项目效果图:
----------------------------------------以下就开始分别实现----------------------------------------
第一步:实现访问手机本地的.mp3音乐文件
- 编写一个音乐类Music(以 周杰伦 - 最长的电影.mp3 为例):编写四个成员变量,并在其中提供对应的set()和get()方法
//音乐的名字(周杰伦 - 最长的电影.mp3),截取后缀获得.mp3的文件,subString("-")截取歌曲的名字
private String name;
//音乐文件的作者
private String artist;
//音乐文件的路径
private String url;
//音乐播放的时间
private int time;
- 编写一个专门访问本地音乐文件的类MusicList,这里用到读取内存的权限,因此别忘了要在AndroidManifest.xml中添加权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
实现步骤:
①编写静态方法getMusicData(),返回值就是查询到的音乐数据集合;
②在该方法中创建ContentRecolver实例,通过上下文的getContentResolver()方法;
③判断获取到的ContentResolver是否为空,如果不为空,调用contentResolver.query (Uri uri, String[] projection,String selection,String[] selectionArgs, String sortOrder)方法查询本地的音乐文件,返回一个Cursor对象。如果Cursor为空,说明没有数据,直接返回null;
④如果有数据,利用Cursor对象的moveToFirst()查询第一条数据,如果不为空,就循环调用moveToNext()方法查询下一条数据直到没有数据为止;
⑤在查询中,通过cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.××))获取到相应的数据,该数据就对应等于Music中的成员变量;’
⑥将查询到的数据赋值到Music对象,完成查询数据的存储,并把该Music对象添加到Music集合,这样就获取到了本地音乐数据集合。
对上面的补充说明(对ContentResolver和ContentProvider了解的可直接跳过)
ContentProvider在android中的作用是对外共享数据,也就是说你可以通过 ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对你应用中的数据进行添删改查。
ContentResolver:提供访问ContentProvider的能力,可以使用ContentResolver读取和操作其他应用程序已经通过ContentProvider暴露出来的数据。
/**
* 从内部存储中读取下载好的后缀名为.mp3的音乐文件,返回值为Music集合
*/
public class MusicList {
public static ArrayList<Music> getMusicData(Context context){
ArrayList<Music> musicList = new ArrayList<Music>();
ContentResolver contentResolver = context.getContentResolver();
if(contentResolver != null){
Cursor cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
if(cursor == null){
return null;
}
if(cursor.moveToFirst()) {
do {
Music music = new Music();
String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
String name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
if ("<unknown>".equals(artist)) {
String[] split = name.split("-");
artist = split[0];
}
int time = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));
String url = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
String isMp3 = name.substring(name.length() - 3, name.length());
if (isMp3.equals("mp3")) {
music.setArtist(artist);
music.setTime(time);
music.setUrl(url);
music.setName(name.substring(0,name.length()-4));
musicList.add(music);
}
} while (cursor.moveToNext());
}
}
return musicList;
}
}
第二步:在主页中将访问的数据显示出来(这里简单点用ListView,用RecycleView当然也可以)
实现步骤:
1.请求运行时权限(Android 6.0及以上系统在使用危险权限时都必须进行运行时权限的处理,这里如果不进行处理,会出现闪退的现象)
//自定义方法requestPermission()
private void requestPermission() {
//创建允许权限的集合
List<String> permissionList = new ArrayList<String>();
//如果该权限没同意,就加入到允许权限的集合
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
//如果允许权限不为空,说明有权限还没申请,就一起申请
if (!permissionList.isEmpty()) {
ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), 1);
}
//权限申请完成之后,才去初始化视图
else {
//适配器的适配工作
initView();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0) {
for (int i = 0; i < grantResults.length; i++) {
int grantResult = grantResults[i];
if (grantResult == PackageManager.PERMISSION_DENIED) {
String s = permissions[i];
Toast.makeText(this, s + "权限被拒绝了", Toast.LENGTH_SHORT).show();
} else {
initView();
}
}
}
}
}
2.设置适配器显示数据
①编写主页的xml(activity_main.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<ListView
android:id="@+id/listView_main"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
②在主页中获取到该控件,并给该控件设置适配器进行显示
MusicAdapter
public class MusicAdapter extends BaseAdapter {
private Context mContext;
private List<Music> mMusicList;
public MusicAdapter(Context context,List<Music> musicList){
mContext = context;
mMusicList = musicList;
}
@Override
public int getCount() {
return mMusicList.size();
}
@Override
public Object getItem(int position) {
return mMusicList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
Music music = mMusicList.get(position);
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.music_item, parent,false);
viewHolder = new ViewHolder();
viewHolder.music_item_name = convertView.findViewById(R.id.music_item_name);
viewHolder.music_item_artist = convertView.findViewById(R.id.music_item_artist);
viewHolder.music_item_time = convertView.findViewById(R.id.music_item_time);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.music_item_name.setText(music.getName());
viewHolder.music_item_artist.setText(music.getArtist());
viewHolder.music_item_time.setText(formatTime(music.getTime()));
return convertView;
}
class ViewHolder {
TextView music_item_name;
TextView music_item_artist;
TextView music_item_time;
}
//将时间转换为××:××格式
private String formatTime(int time) {
int ms2s = (time / 1000);
int minute = ms2s / 60;
int second = ms2s % 60;
return String.format("%02d:%02d", minute, second);
}
}
music.item.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:padding="16dp">
<TextView
android:id="@+id/music_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp">
<TextView
android:id="@+id/music_item_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/music_item_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/music_item_artist" />
</RelativeLayout>
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
private ArrayList<Music> arrayList;
private MusicAdapter mMusicAdapter;
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
requestPermission();
}
private void initView() {
arrayList = MusicList.getMusicData(this);
mListView = findViewById(R.id.listView_main);
mMusicAdapter = new MusicAdapter(this, arrayList);
mListView.setAdapter(mMusicAdapter);
//实现页面跳转,将当前点击的音乐条目传过去给音乐播放页面,这样它才知道播放哪条音乐
mListView.setOnItemClickListener(new AdapterView.OnItemClick