说一下代码在用的时候注意事项以及在运行的时候可能遇到的问题:
首先代码可以在创建相应文件后直接复制,这个案例用到了RecyclerView,所以需要先添加依赖。添加下面两个:
implementation ‘com.android.support:recyclerview-v7:27.1.1′
implementation ‘com.android.support:cardview-v7:27.1.1′
具体版本看自己的软件。
一定要在AndroidManifest.xml加上:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
下面代码部分也给出了。
接下来说一下运行时的问题:
(1)开发本项目最大的问题在于API版本问题,这也是Android开发中最头疼的问题,在遇到问题的时候一定要查看下自己的版本。
(2)在模拟器上打开项目时为空白列表,原因在于模拟器没有音乐文件,可以先利用模拟器下载音乐文件,之后再打开就能显示了 。下载文件的时候不要直接将文件从电脑拖入模拟器,虽然有文件,但是不被应用识别,我在运行的时候就遇到了这个问题,音乐文件最好从网上下载。
(3)如果运行时模拟器不能打开app,报下面错误:
java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=13081, uid=10084 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
一定要确定加入了我上面所说的那行代码,然后打开模拟器设置(settings)找到应用管理(apps¬ifications),选择应用权限(APP permissions )找到自己的软件,打开文件读取权限。看下面第一张图,附上两张app运行的截图:
下面的的源代码来自:https://gitee.com/happyanimee/localmusic
这里面有整个项目的文件,本文的主要任务是总结使用时候所遇到的问题以及主要代码供大家学习。里面所用到的图片在文末下载。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@mipmap/bg2"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="70dp"android:layout_alignParentBottom="true"android:id="@+id/local_music_bottomlayout"android:background="#33EEEEEE"><ImageViewandroid:layout_width="match_parent"android:layout_height="0.5dp"android:background="#9933FA"/><ImageViewandroid:layout_width="60dp"android:layout_height="60dp"android:src="@mipmap/icon_song"android:layout_centerVertical="true"android:background="@mipmap/a1"android:layout_marginLeft="10dp"android:id="@+id/local_music_bottom_iv_icon"/><TextViewandroid:id="@+id/local_music_bottom_tv_song"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""android:layout_toRightOf="@id/local_music_bottom_iv_icon"android:layout_marginTop="10dp"android:layout_marginLeft="10dp"android:textSize="16sp"android:textStyle="bold"/><TextViewandroid:id="@+id/local_music_bottom_tv_singer"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""android:textSize="12sp"android:layout_below="@id/local_music_bottom_tv_song"android:layout_alignLeft="@id/local_music_bottom_tv_song"android:layout_marginTop="10dp"/><ImageViewandroid:id="@+id/local_music_bottom_iv_next"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:src="@mipmap/icon_next"android:layout_alignParentRight="true"android:layout_marginRight="10dp"/><ImageViewandroid:id="@+id/local_music_bottom_iv_play"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:src="@mipmap/icon_play"android:layout_toLeftOf="@id/local_music_bottom_iv_next"android:layout_marginRight="20dp"/><ImageViewandroid:id="@+id/local_music_bottom_iv_last"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:src="@mipmap/icon_last"android:layout_toLeftOf="@id/local_music_bottom_iv_play"android:layout_marginRight="20dp"/></RelativeLayout>
<android.support.v7.widget.RecyclerViewandroid:id="@+id/local_music_rv"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_above="@id/local_music_bottomlayout">
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
item_local_music.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginRight="10dp"android:layout_marginLeft="10dp"android:layout_marginTop="10dp"app:contentPadding="10dp"app:cardCornerRadius="10dp"app:cardElevation="1dp"app:cardBackgroundColor="@color/colorPink"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/item_local_music_num"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="1"android:layout_centerVertical="true"android:textSize="24sp"android:textStyle="bold"/><TextViewandroid:id="@+id/item_local_music_song"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""android:textSize="18sp"android:textStyle="bold"android:layout_toRightOf="@id/item_local_music_num"android:singleLine="true"android:layout_marginLeft="20dp"/><TextViewandroid:id="@+id/item_local_music_singer"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""android:layout_below="@id/item_local_music_song"android:layout_alignLeft="@id/item_local_music_song"android:layout_marginTop="10dp"android:textSize="14sp"android:textColor="#888"/><TextViewandroid:id="@+id/item_local_music_line"android:layout_width="2dp"android:layout_height="18dp"android:background="#888"android:layout_toRightOf="@id/item_local_music_singer"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"android:layout_alignTop="@id/item_local_music_singer"/><TextViewandroid:id="@+id/item_local_music_album"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text=""android:layout_toRightOf="@id/item_local_music_line"android:layout_alignTop="@id/item_local_music_singer"android:textSize="14sp"android:textColor="#888"android:ellipsize="end"android:singleLine="true"/><TextViewandroid:id="@+id/item_local_music_durtion"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/item_local_music_singer"android:layout_alignParentRight="true"android:text=""android:textSize="14sp"android:textColor="#888"/></RelativeLayout>
</android.support.v7.widget.CardView>
MainActivity.java
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class MainActivity extends AppCompatActivity implements View.OnClickListener{ImageView nextIv,playIv,lastIv,albumIv;TextView singerTv,songTv;RecyclerView musicRv;// 数据源List<LocalMusicBean>mDatas;private LocalMusicAdapter adapter;// 记录当前正在播放的音乐的位置int currnetPlayPosition = -1;// 记录暂停音乐时进度条的位置int currentPausePositionInSong = 0;MediaPlayer mediaPlayer;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();mediaPlayer = new MediaPlayer();mDatas = new ArrayList<>();
// 创建适配器对象adapter = new LocalMusicAdapter(this, mDatas);musicRv.setAdapter(adapter);
// 设置布局管理器LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);musicRv.setLayoutManager(layoutManager);
// 加载本地数据源loadLocalMusicData();
// 设置每一项的点击事件setEventListener();}private void setEventListener() {/* 设置每一项的点击事件*/adapter.setOnItemClickListener(new LocalMusicAdapter.OnItemClickListener() {@Overridepublic void OnItemClick(View view, int position) {currnetPlayPosition = position;LocalMusicBean musicBean = mDatas.get(position);playMusicInMusicBean(musicBean);}});}public void playMusicInMusicBean(LocalMusicBean musicBean) {/*根据传入对象播放音乐*///设置底部显示的歌手名称和歌曲名singerTv.setText(musicBean.getSinger());songTv.setText(musicBean.getSong());stopMusic();
// 重置多媒体播放器mediaPlayer.reset();
// 设置新的播放路径try {mediaPlayer.setDataSource(musicBean.getPath());String albumArt = musicBean.getAlbumArt();Log.i("lsh123", "playMusicInMusicBean: albumpath=="+albumArt);Bitmap bm = BitmapFactory.decodeFile(albumArt);Log.i("lsh123", "playMusicInMusicBean: bm=="+bm);albumIv.setImageBitmap(bm);playMusic();} catch (IOException e) {e.printStackTrace();}}/** 点击播放按钮播放音乐,或者暂停从新播放* 播放音乐有两种情况:* 1.从暂停到播放* 2.从停止到播放* */private void playMusic() {/* 播放音乐的函数*/if (mediaPlayer!=null&&!mediaPlayer.isPlaying()) {if (currentPausePositionInSong == 0) {try {mediaPlayer.prepare();mediaPlayer.start();} catch (IOException e) {e.printStackTrace();}}else{
// 从暂停到播放mediaPlayer.seekTo(currentPausePositionInSong);mediaPlayer.start();}playIv.setImageResource(R.mipmap.icon_pause);}}private void pauseMusic() {/* 暂停音乐的函数*/if (mediaPlayer!=null&&mediaPlayer.isPlaying()) {currentPausePositionInSong = mediaPlayer.getCurrentPosition();mediaPlayer.pause();playIv.setImageResource(R.mipmap.icon_play);}}private void stopMusic() {/* 停止音乐的函数*/if (mediaPlayer!=null) {currentPausePositionInSong = 0;mediaPlayer.pause();mediaPlayer.seekTo(0);mediaPlayer.stop();playIv.setImageResource(R.mipmap.icon_play);}}@Overrideprotected void onDestroy() {super.onDestroy();stopMusic();}private void loadLocalMusicData() {/* 加载本地存储当中的音乐mp3文件到集合当中*/
// 1.获取ContentResolver对象ContentResolver resolver = getContentResolver();
// 2.获取本地音乐存储的Uri地址Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
// 3 开始查询地址Cursor cursor = resolver.query(uri, null, null, null, null);
// 4.遍历Cursorint id = 0;while (cursor.moveToNext()) {String song = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));String singer = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));String album = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));id++;String sid = String.valueOf(id);String path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");String time = sdf.format(new Date(duration));
// 获取专辑图片主要是通过album_id进行查询String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));String albumArt = getAlbumArt(album_id);
// 将一行当中的数据封装到对象当中LocalMusicBean bean = new LocalMusicBean(sid, song, singer, album, time, path,albumArt);mDatas.add(bean);}
// 数据源变化,提示适配器更新adapter.notifyDataSetChanged();}private String getAlbumArt(String album_id) {String mUriAlbums = "content://media/external/audio/albums";String[] projection = new String[]{"album_art"};Cursor cur = this.getContentResolver().query(Uri.parse(mUriAlbums + "/" + album_id),projection, null, null, null);String album_art = null;if (cur.getCount() > 0 && cur.getColumnCount() > 0) {cur.moveToNext();album_art = cur.getString(0);}cur.close();cur = null;return album_art;}private void initView() {/* 初始化控件的函数*/nextIv = findViewById(R.id.local_music_bottom_iv_next);playIv = findViewById(R.id.local_music_bottom_iv_play);lastIv = findViewById(R.id.local_music_bottom_iv_last);albumIv = findViewById(R.id.local_music_bottom_iv_icon);singerTv = findViewById(R.id.local_music_bottom_tv_singer);songTv = findViewById(R.id.local_music_bottom_tv_song);musicRv = findViewById(R.id.local_music_rv);nextIv.setOnClickListener(this);lastIv.setOnClickListener(this);playIv.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.local_music_bottom_iv_last:if (currnetPlayPosition ==0) {Toast.makeText(this,"已经是第一首了,没有上一曲!",Toast.LENGTH_SHORT).show();return;}currnetPlayPosition = currnetPlayPosition-1;LocalMusicBean lastBean = mDatas.get(currnetPlayPosition);playMusicInMusicBean(lastBean);break;case R.id.local_music_bottom_iv_next:if (currnetPlayPosition ==mDatas.size()-1) {Toast.makeText(this,"已经是最后一首了,没有下一曲!",Toast.LENGTH_SHORT).show();return;}currnetPlayPosition = currnetPlayPosition+1;LocalMusicBean nextBean = mDatas.get(currnetPlayPosition);playMusicInMusicBean(nextBean);break;case R.id.local_music_bottom_iv_play:if (currnetPlayPosition == -1) {
// 并没有选中要播放的音乐Toast.makeText(this,"请选择想要播放的音乐",Toast.LENGTH_SHORT).show();return;}if (mediaPlayer.isPlaying()) {
// 此时处于播放状态,需要暂停音乐pauseMusic();}else {
// 此时没有播放音乐,点击开始播放音乐playMusic();}break;}}
}
LocalMusicBean.java
public class LocalMusicBean {private String id; //歌曲idprivate String song; //歌曲名称private String singer; //歌手名称private String album; //专辑名称private String duration; //歌曲时长private String path; //歌曲路径private String albumArt; //专辑地址public LocalMusicBean() {}public LocalMusicBean(String id, String song, String singer, String album, String duration, String path,String albumArt) {this.id = id;this.song = song;this.singer = singer;this.album = album;this.duration = duration;this.path = path;this.albumArt = albumArt;}public String getAlbumArt() {return albumArt;}public void setAlbumArt(String albumArt) {this.albumArt = albumArt;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getSong() {return song;}public void setSong(String song) {this.song = song;}public String getSinger() {return singer;}public void setSinger(String singer) {this.singer = singer;}public String getAlbum() {return album;}public void setAlbum(String album) {this.album = album;}public String getDuration() {return duration;}public void setDuration(String duration) {this.duration = duration;}public String getPath() {return path;}public void setPath(String path) {this.path = path;}
}
LocalMusicAdapter.java
import android.content.Context;import android.support.annotation.NonNull;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import java.util.List;public class LocalMusicAdapter extends RecyclerView.Adapter<LocalMusicAdapter.LocalMusicViewHolder>{Context context;List<LocalMusicBean>mDatas;OnItemClickListener onItemClickListener;public void setOnItemClickListener(OnItemClickListener onItemClickListener) {this.onItemClickListener = onItemClickListener;}public interface OnItemClickListener{public void OnItemClick(View view,int position);}public LocalMusicAdapter(Context context, List<LocalMusicBean> mDatas) {this.context = context;this.mDatas = mDatas;}@NonNull@Overridepublic LocalMusicViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.item_local_music,parent,false);LocalMusicViewHolder holder = new LocalMusicViewHolder(view);return holder;}@Overridepublic void onBindViewHolder(@NonNull LocalMusicViewHolder holder, final int position) {LocalMusicBean musicBean = mDatas.get(position);holder.idTv.setText(musicBean.getId());holder.songTv.setText(musicBean.getSong());holder.singerTv.setText(musicBean.getSinger());holder.albumTv.setText(musicBean.getAlbum());holder.timeTv.setText(musicBean.getDuration());holder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {onItemClickListener.OnItemClick(v,position);}});}@Overridepublic int getItemCount() {return mDatas.size();}class LocalMusicViewHolder extends RecyclerView.ViewHolder{TextView idTv,songTv,singerTv,albumTv,timeTv;public LocalMusicViewHolder(View itemView) {super(itemView);idTv = itemView.findViewById(R.id.item_local_music_num);songTv = itemView.findViewById(R.id.item_local_music_song);singerTv = itemView.findViewById(R.id.item_local_music_singer);albumTv = itemView.findViewById(R.id.item_local_music_album);timeTv = itemView.findViewById(R.id.item_local_music_durtion);}}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.a33119.music"><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>
打包后apk:本地音乐播放器APP