Android MediaPlayer 本地音乐播放器 运行截图 为了不浪费您的时间,先看一下运行的效果图, 一进去先进行音乐扫描,然后列表展示
为了不浪费您的时间,先看一下运行的效果图,
一进去先进行音乐扫描,然后列表展示出来,点击即可播放。
演示视频地址
源码地址GitHub 项目地址
这个给不想浪费时间往下看的朋友,只因你的时间很宝贵。
至于为什么写一个这样的Demo呢,因为有很多人学习Android就是对于手机应用感兴趣,而网络上的很应用源码,很难的开源代码小白看不懂,小白能看懂的,有些博主又要用积分下载,痛定思痛,索性自己写一个,当然在写的过程中查阅了网络的资料,也加入了自己的想法,希望能帮到想在音乐播放器这方面有想法的朋友,好了,话不多说,进入正题.:
代码解释 项目配置1.权限配置:
打开AndroidManifest.xml
2.依赖引入:
先打开工程的build.gradle,
加入如下图中的代码
item_music_rv_list.xml
布局其实没有什么好讲解的,就是显示和隐藏的控制。
样式文件:
rounded_corners.xml 水波纹效果的xml文件
在bg_white.xml中调用了rounded_corners.xml
bg_white.xml代码如下:
扫描按钮的背景图
scan_finish_btn_bg_pressed.9
scan_finish_btn_bg_nORMal.9
selector_scan_btn.xml使用上面两个图片的按钮点击样式
selector_scan_btn这个样式文件,在activity_main中用到
seekbar_style.xml 进度条样式
thumb.xml 进度条滑块样式
上面两个关于进度条的样式也在activity_main.xml中用到
然后是values下面的styles.xml文件
@color/white
@color/white
@color/colorAccent
样式和布局讲完了,接下来是工具类:
Constant.java 缓存字符工具类,因为现在只在MainActivity中用到,所以提现不出它的优势,代码如下:
public class Constant {
public final static String MUSIC_DATA_FIRST = "musicDataFirst";
}
DateUtil.java 时间转换工具类:
public class DateUtil {
//获取当前完整的日期和时间
public static String getNowDateTime(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
return sdf.format(new Date());
}
//获取当前日期
public static String getNowDate(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
return sdf.format(new Date());
}
//获取当前时间
public static String getNowTime(){
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
return sdf.format(new Date());
}
//获取当前时间不包含秒
public static String getNowTimeM(){
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
return sdf.format(new Date());
}
//转换当前时间不包含时
public static String parseTime(int oldTime) {
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");// 时间格式
String newTime = sdf.format(new Date(oldTime));
return newTime;
}
//获取当前日期(精确到毫秒)
public static String getNowTimeDetail(){
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss:SSS");
return sdf.format(new Date());
}
//获取当前日期是星期几
public static String getWeekOfDate(Date date) {
String[] weekDays = { "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六" };
Calendar cal = Calendar.getInstance();
cal.setTime(date);
int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
if (w 10){// 10位的秒级别的时间戳
times = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(time * 1000));
}else {// 13位的秒级别的时间戳
times = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time);
}
return times;
}
//将时间字符串转为时间戳字符串
public static String getStringTimestamp(String time) {
String timestamp = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Long longTime = sdf.parse(time).getTime()/1000;
timestamp = Long.toString(longTime);
} catch (ParseException e) {
e.printStackTrace();
}
return timestamp;
}
//将长整型时间转为为分秒
public static String time(long millionSeconds) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss");
Calendar c = Calendar.getInstance();
c.setTimeInMillis(millionSeconds);
return simpleDateFormat.format(c.getTime());
}
//将长度转换为时间
public static StringBuilder mFormatBuilder = new StringBuilder();
public static Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
public static String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
mFormatBuilder.setLength(0);
if (hours > 0) {
return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
} else {
return mFormatter.format("%02d:%02d", minutes, seconds).toString();
}
}
}
MusicUtils.java 音乐扫描工具类
public class MusicUtils {
public static List getMusicData(Context context) {
List list = new ArrayList();
// 媒体库查询语句(写一个工具类MusicUtils)
Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null,
null, MediaStore.Audio.Audiocolumns.IS_MUSIC);
if (cursor != null) {
while (cursor.moveToNext()) {
Song song = new Song();
song.song = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));//歌曲名称
song.singer = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));//歌手
song.album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));//专辑名
song.path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));//歌曲路径
song.duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));//歌曲时长
song.size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));//歌曲大小
if (song.size > 1000 * 800) {
// 注释部分是切割标题,分离出歌曲名和歌手 (本地媒体库读取的歌曲信息不规范)
if (song.song.contains("-")) {
String[] str = song.song.split("-");
song.singer = str[0];
song.song = str[1];
}
list.add(song);
}
}
// 释放资源
cursor.close();
}
return list;
}
//专辑图片
private static String imgUrl(Context context){
String album_art= null;
String[] mediaColumns1 = new String[] {MediaStore.Audio.Albums.ALBUM_ART, MediaStore.Audio.Albums.ALBUM};
Cursor cursor1 = context.getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, mediaColumns1, null, null,
null);
if (cursor1 != null) {
cursor1.moveToFirst();
do {
album_art = cursor1.getString(0);
if (album_art != null) {
Log.d("ALBUM_ART", album_art);
}
String album = cursor1.getString(1);
if (album != null) {
Log.d("ALBUM_ART", album);
}
} while (cursor1.moveToNext());
cursor1.close();
}
return album_art;
}
public static String formatTime(int time) {
if (time / 1000 % 60 < 10) {
return time / 1000 / 60 + ":0" + time / 1000 % 60;
} else {
return time / 1000 / 60 + ":" + time / 1000 % 60;
}
}
ObjectUtil.java 对象工具类,用于判空
public final class ObjectUtils {
private ObjectUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
public static boolean isEmpty(final Object obj) {
if (obj == null) {
return true;
}
if (obj.getClass().isArray() && Array.getLength(obj) == 0) {
return true;
}
if (obj instanceof CharSequence && obj.toString().length() == 0) {
return true;
}
if (obj instanceof Collection && ((Collection) obj).isEmpty()) {
return true;
}
if (obj instanceof Map && ((Map) obj).isEmpty()) {
return true;
}
if (obj instanceof SimpleArrayMap && ((SimpleArrayMap) obj).isEmpty()) {
return true;
}
if (obj instanceof SparseArray && ((SparseArray) obj).size() == 0) {
return true;
}
if (obj instanceof SparseBooleanArray && ((SparseBooleanArray) obj).size() == 0) {
return true;
}
if (obj instanceof SparseIntArray && ((SparseIntArray) obj).size() == 0) {
return true;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if (obj instanceof SparseLongArray && ((SparseLongArray) obj).size() == 0) {
return true;
}
}
if (obj instanceof LongSparseArray && ((LongSparseArray) obj).size() == 0) {
return true;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (obj instanceof android.util.LongSparseArray
&& ((android.util.LongSparseArray) obj).size() == 0) {
return true;
}
}
return false;
}
public static boolean isEmpty(final CharSequence obj) {
return obj == null || obj.toString().length() == 0;
}
public static boolean isEmpty(final Collection obj) {
return obj == null || obj.isEmpty();
}
public static boolean isEmpty(final Map obj) {
return obj == null || obj.isEmpty();
}
public static boolean isEmpty(final SimpleArrayMap obj) {
return obj == null || obj.isEmpty();
}
public static boolean isEmpty(final SparseArray obj) {
return obj == null || obj.size() == 0;
}
public static boolean isEmpty(final SparseBooleanArray obj) {
return obj == null || obj.size() == 0;
}
public static boolean isEmpty(final SparseIntArray obj) {
return obj == null || obj.size() == 0;
}
public static boolean isEmpty(final LongSparseArray obj) {
return obj == null || obj.size() == 0;
}
@Requiresapi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public static boolean isEmpty(final SparseLongArray obj) {
return obj == null || obj.size() == 0;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public static boolean isEmpty(final android.util.LongSparseArray obj) {
return obj == null || obj.size() == 0;
}
public static boolean isNotEmpty(final Object obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final CharSequence obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final Collection obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final Map obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final SimpleArrayMap obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final SparseArray obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final SparseBooleanArray obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final SparseIntArray obj) {
return !isEmpty(obj);
}
public static boolean isNotEmpty(final LongSparseArray obj) {
return !isEmpty(obj);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public static boolean isNotEmpty(final SparseLongArray obj) {
return !isEmpty(obj);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public static boolean isNotEmpty(final android.util.LongSparseArray obj) {
return !isEmpty(obj);
}
public static boolean equals(final Object o1, final Object o2) {
return o1 == o2 || (o1 != null && o1.equals(o2));
}
public static void requireNonNull(final Object... objects) {
if (objects == null) throw new NullPointerException();
for (Object object : objects) {
if (object == null) throw new NullPointerException();
}
}
public static T getOrDefault(final T object, final T defaultObject) {
if (object == null) {
return defaultObject;
}
return object;
}
public static int hashCode(final Object o) {
return o != null ? o.hashCode() : 0;
}
}
SPUtils.java 缓存读写工具类,配合Constant使效果更佳
public class SPUtils {
private static final String NAME="config";
public static void putBoolean(String key, boolean value, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().putBoolean(key, value).commit();
}
public static boolean getBoolean(String key, boolean defValue, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
return sp.getBoolean(key, defValue);
}
public static void putString(String key, String value, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().putString(key, value).commit();
}
public static String getString(String key, String defValue, Context context) {
if(context!=null){
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
return sp.getString(key, defValue);
}
return "";
}
public static void putInt(String key, int value, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().putInt(key, value).commit();
}
public static int getInt(String key, int defValue, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
return sp.getInt(key, defValue);
}
public static void remove(String key, Context context) {
SharedPreferences sp = context.getSharedPreferences(NAME,
Context.MODE_PRIVATE);
sp.edit().remove(key).commit();
}
}
StatusBarUtil.java 状态栏工具类:
public class StatusBarUtil {
@TargetApi(19)
public static void transparencyBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = activity.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
public static void setStatusBarColor(Activity activity, int colorId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(activity.getResources().getColor(colorId));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//使用SystemBarTint库使4.4版本状态栏变色,需要先将状态栏设置为透明
transparencyBar(activity);
SystemBarTintManager tintManager = new SystemBarTintManager(activity);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(colorId);
}
}
public static int StatusBarLightMode(Activity activity) {
int result = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (MIUISetStatusBarLightMode(activity, true)) {
result = 1;
} else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {
result = 2;
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
result = 3;
}
}
return result;
}
public static void StatusBarLightMode(Activity activity, int type) {
if (type == 1) {
MIUISetStatusBarLightMode(activity, true);
} else if (type == 2) {
FlymeSetStatusBarLightMode(activity.getWindow(), true);
} else if (type == 3) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
public static void StatusBarDarkMode(Activity activity, int type) {
if (type == 1) {
MIUISetStatusBarLightMode(activity, false);
} else if (type == 2) {
FlymeSetStatusBarLightMode(activity.getWindow(), false);
} else if (type == 3) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
boolean result = false;
if (window != null) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class
.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class
.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
result = true;
} catch (Exception e) {
}
}
return result;
}
public static boolean MIUISetStatusBarLightMode(Activity activity, boolean dark) {
boolean result = false;
Window window = activity.getWindow();
if (window != null) {
Class clazz = window.getClass();
try {
int darkModeFlag = 0;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) {
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体
} else {
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
}
result = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上
if (dark) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
} catch (Exception e) {
}
}
return result;
}
}
ToastUtils.java Toast消息类,原来的太麻烦了,
public class ToastUtils {
public static void showLongToast(Context context, CharSequence llw) {
Toast.makeText(context.getApplicationContext(), llw, Toast.LENGTH_LONG).show();
}
public static void showShortToast(Context context, CharSequence llw) {
Toast.makeText(context.getApplicationContext(), llw, Toast.LENGTH_SHORT).show();
}
}
工具类介绍完成,接下来是数据模型:
Song.java 歌曲工具类:
public class Song {
public String singer;
public String song;
public String album;
public String album_art;
public String path;
public int duration;
public long size;
public String getSinger() {
return singer;
}
public void setSinger(String singer) {
this.singer = singer;
}
public String getSong() {
return song;
}
public void setSong(String song) {
this.song = song;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
public String getAlbum() {
return album;
}
public void setAlbum(String album) {
this.album = album;
}
public String getAlbum_art() {
return album_art;
}
public void setAlbum_art(String album_art) {
this.album_art = album_art;
}
}
MusicListAdapter.java 歌曲列表适配器
public class MusicListAdapter extends BaseQuickAdapter{
public MusicListAdapter(int layoutResId, @Nullable List data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, Song item) {
//给控件赋值
int duration = item.duration;
String time = MusicUtils.formatTime(duration);
helper.setText(R.id.tv_song_name,item.getSong().trim())//歌曲名称
.setText(R.id.tv_singer,item.getSinger()+" - "+item.getAlbum())//歌手 - 专辑
.setText(R.id.tv_duration_time,time)//歌曲时间
//歌曲序号,因为getAdapterPosition得到的位置是从0开始,故而加1,
//是因为位置和1都是整数类型,直接赋值给TextView会报错,故而拼接了""
.setText(R.id.tv_position,helper.getAdapterPosition()+1+"");
helper.addOnClickListener(R.id.item_music);//给item添加点击事件,点击之后传递数据到播放页面或者在本页面进行音乐播放
}
}
然后是主角出场了:
MainActivity.java 主要逻辑代码都在里面:
注释也已经写好了,我就不过多的啰嗦了,代码如下:
package com.llw.music;
import android.Manifest;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.llw.music.adapter.MusicListAdapter;
import com.llw.music.model.Song;
import com.llw.music.utils.Constant;
import com.llw.music.utils.MusicUtils;
import com.llw.music.utils.ObjectUtils;
import com.llw.music.utils.SPUtils;
import com.llw.music.utils.StatusBarUtil;
import com.llw.music.utils.ToastUtils;
import com.tbruyelle.rxpermissions2.RxPermissions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static com.llw.music.utils.DateUtil.parseTime;
public class MainActivity extends AppCompatActivity implements MediaPlayer.OnCompletionListener {
@BindView(R.id.rv_music)
RecyclerView rvMusic;
@BindView(R.id.btn_scan)
Button btnScan;
@BindView(R.id.scan_lay)
LinearLayout scanLay;
@BindView(R.id.tv_clear_list)
TextView tvClearList;
@BindView(R.id.tv_title)
TextView tvTitle;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.tv_play_time)
TextView tvPlayTime;
@BindView(R.id.time_seekBar)
SeekBar timeSeekBar;
@BindView(R.id.tv_total_time)
TextView tvTotalTime;
@BindView(R.id.btn_previous)
ImageView btnPrevious;
@BindView(R.id.btn_play_or_pause)
ImageView btnPlayOrPause;
@BindView(R.id.btn_next)
ImageView btnNext;
@BindView(R.id.tv_play_song_info)
TextView tvPlaySongInfo;
@BindView(R.id.play_state_img)
ImageView playStateImg;
@BindView(R.id.play_state_lay)
LinearLayout playStateLay;
private MusicListAdapter mAdapter;//歌曲适配器
private List mList;//歌曲列表
private RxPermissions rxPermissions;//权限请求
private MediaPlayer mediaPlayer;//音频播放器
private String musicData = null;
// 记录当前播放歌曲的位置
public int mCurrentPosition;
private static final int INTERNAL_TIME = 1000;// 音乐进度间隔时间
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
// 展示给进度条和当前时间
int progress = mediaPlayer.getCurrentPosition();
timeSeekBar.setProgress(progress);
tvPlayTime.setText(parseTime(progress));
// 继续定时发送数据
updateProgress();
return true;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
StatusBarUtil.StatusBarLightMode(this);
rxPermissions = new RxPermissions(this);//使用前先实例化
timeSeekBar.setOnSeekBarChangeListener(seekBarChangeListener);//滑动条监听
musicData = SPUtils.getString(Constant.MUSIC_DATA_FIRST, "yes", this);
if (musicData.equals("no")) {//说明是第一次打开APP,未进行扫描
scanLay.setVisibility(View.GoNE);
initMusic();
} else {
scanLay.setVisibility(View.VISIBLE);
}
}
private void permissionRequest() {//使用这个框架需要制定jdk版本,建议用1.8
rxPermissions.request(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
.subscribe(granted -> {
if (granted) {//请求成功之后开始扫描
initMusic();
} else {//失败时给一个提示
ToastUtils.showShortToast(MainActivity.this, "未授权");
}
});
}
//获取音乐列表
private void initMusic() {
mList = new ArrayList();//实例化
//数据赋值
mList = MusicUtils.getMusicData(this);//将扫描到的音乐赋值给音乐列表
if (!ObjectUtils.isEmpty(mList) && mList != null) {
scanLay.setVisibility(View.GONE);
SPUtils.putString(Constant.MUSIC_DATA_FIRST, "no", this);
}
mAdapter = new MusicListAdapter(R.layout.item_music_rv_list, mList);//指定适配器的布局和数据源
//线性布局管理器,可以设置横向还是纵向,RecyclerView默认是纵向的,所以不用处理,如果不需要设置方向,代码还可以更加的精简如下
rvMusic.setLayoutManager(new LinearLayoutManager(this));
//如果需要设置方向显示,则将下面代码注释去掉即可
// LinearLayoutManager manager = new LinearLayoutManager(this);
// manager.setOrientation(RecyclerView.HORIZONTAL);
// rvMusic.setLayoutManager(manager);
//设置适配器
rvMusic.setAdapter(mAdapter);
//item的点击事件
mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
if (view.getId() == R.id.item_music) {
mCurrentPosition = position;
changeMusic(mCurrentPosition);
// mAdapter.notifyItemRangeChanged(0, mList.size());
}
}
});
//设置背景样式
initStyle();
}
private void initStyle() {
//toolbar背景变透明
toolbar.setBackgroundColor(getResources().getColor(R.color.half_transparent));
//文字变白色
tvTitle.setTextColor(getResources().getColor(R.color.white));
tvClearList.setTextColor(getResources().getColor(R.color.white));
StatusBarUtil.transparencyBar(this);
}
@OnClick({R.id.tv_clear_list, R.id.btn_scan, R.id.btn_previous, R.id.btn_play_or_pause, R.id.btn_next})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.tv_clear_list://清空数据
mList.clear();
mAdapter.notifyDataSetChanged();
SPUtils.putString(Constant.MUSIC_DATA_FIRST, "yes", this);
scanLay.setVisibility(View.VISIBLE);
toolbar.setBackgroundColor(getResources().getColor(R.color.white));
StatusBarUtil.StatusBarLightMode(this);
tvTitle.setTextColor(getResources().getColor(R.color.black));
tvClearList.setTextColor(getResources().getColor(R.color.black));
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
mediaPlayer.reset();
}
break;
case R.id.btn_scan://扫描本地歌曲
permissionRequest();
break;
case R.id.btn_previous://上一曲
changeMusic(--mCurrentPosition);//当前歌曲位置减1
break;
case R.id.btn_play_or_pause://播放或者暂停
// 首次点击播放按钮,默认播放第0首,下标从0开始
if (mediaPlayer == null) {
changeMusic(0);
} else {
if (mediaPlayer.isPlaying()) {
mediaPlayer.pause();
btnPlayOrPause.setBackground(getResources().getDrawable(R.mipmap.icon_pause));
playStateImg.setBackground(getResources().getDrawable(R.mipmap.list_play_state));
//如果你是用TextView的leftDrawable设置的图片,在代码里面就可以通过下面代码来动态更换
// Drawable leftImg = getResources().getDrawable(R.mipmap.list_play_state);
// leftImg.setBounds(0, 0, leftImg.getMinimumWidth(), leftImg.getMinimumHeight());
// tvPlaySongInfo.setCompoundDrawables(leftImg, null, null, null);
} else {
mediaPlayer.start();
btnPlayOrPause.setBackground(getResources().getDrawable(R.mipmap.icon_play));
playStateImg.setBackground(getResources().getDrawable(R.mipmap.list_pause_state));
}
}
break;
case R.id.btn_next://下一曲
changeMusic(++mCurrentPosition);//当前歌曲位置加1
break;
}
}
//切歌
private void changeMusic(int position) {
Log.e("MainActivity", "position:" + position);
if (position mList.size() - 1) {
mCurrentPosition = position = 0;
}
Log.e("MainActivity", "position:" + position);
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
try {
// 切歌之前先重置,释放掉之前的资源
mediaPlayer.reset();
// 设置播放源
Log.d("Music", mList.get(position).path);
mediaPlayer.setDataSource(mList.get(position).path);
tvPlaySongInfo.setText("歌名: " + mList.get(position).song +
" 歌手: " + mList.get(position).singer);
// Glide.with(this).load(mList.get(position).album_art).into(songImage);
tvPlaySongInfo.setSelected(true);//跑马灯效果
playStateLay.setVisibility(View.VISIBLE);
// 开始播放前的准备工作,加载多媒体资源,获取相关信息
mediaPlayer.prepare();
// 开始播放
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
// 切歌时重置进度条并展示歌曲时长
timeSeekBar.setProgress(0);
timeSeekBar.setMax(mediaPlayer.getDuration());
tvTotalTime.setText(parseTime(mediaPlayer.getDuration()));
updateProgress();
if (mediaPlayer.isPlaying()) {
btnPlayOrPause.setBackground(getResources().getDrawable(R.mipmap.icon_play));
playStateImg.setBackground(getResources().getDrawable(R.mipmap.list_pause_state));
} else {
btnPlayOrPause.setBackground(getResources().getDrawable(R.mipmap.icon_pause));
playStateImg.setBackground(getResources().getDrawable(R.mipmap.list_play_state));
}
}
private void updateProgress() {
// 使用Handler每间隔1s发送一次空消息,通知进度条更新
Message msg = Message.obtain();// 获取一个现成的消息
// 使用MediaPlayer获取当前播放时间除以总时间的进度
int progress = mediaPlayer.getCurrentPosition();
msg.arg1 = progress;
mHandler.sendMessageDelayed(msg, INTERNAL_TIME);
}
//滑动条监听
SeekBar.OnSeekBarChangeListener seekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
// 当手停止拖拽进度条时执行该方法
// 获取拖拽进度
// 将进度对应设置给MediaPlayer
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
mediaPlayer.seekTo(progress);
}
};
//播放完成之后自动下一曲
@Override
public void onCompletion(MediaPlayer mp) {
changeMusic(++mCurrentPosition);
}
}
gitHub 项目源码
这个地址是为了不浪费你的时间往上滑动。
以上就是所有代码了,如果你写的过程中,某些地方为红色,就是没有文件资源,或者是依赖。有任何问题评论留言,我会在第一时间回复你,感谢你的阅读,希望对你有所帮助,山高水长,后会有期~
--结束END--
本文标题: Android MediaPlayer 音乐播放器扫描 本地音乐、上一曲、下一曲切歌、播放本地音乐
本文链接: https://www.lsjlt.com/news/29153.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-01-21
2023-10-28
2023-10-28
2023-10-27
2023-10-27
2023-10-27
2023-10-27
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0