样式文件:
rounded_corners.xml 水波纹效果的xml文件
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape android:shape="rectangle"> <solid android:color="#18ffc400"/> </shape> </item> <item android:state_focused="true" android:state_enabled="true"> <shape android:shape="rectangle"> <solid android:color="#0f000000"/> </shape> </item> </selector>
在bg_white.xml中调用了rounded_corners.xml
bg_white.xml代码如下:
<?xml version="1.0" encoding="utf-8"?> <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#20000000" android:drawable="@drawable/rounded_corners"/>
扫描按钮的背景图
scan_finish_btn_bg_pressed.9
scan_finish_btn_bg_normal.9
selector_scan_btn.xml使用上面两个图片的按钮点击样式
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/scan_finish_btn_bg_pressed" /> <item android:drawable="@drawable/scan_finish_btn_bg_normal" /> </selector>
selector_scan_btn这个样式文件,在activity_main中用到
seekbar_style.xml 进度条样式
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <shape> <corners android:radius="5dp" /> <solid android:color="#B6C4F9" /> </shape> </item> <item android:id="@android:id/secondaryProgress"> <clip> <shape> <corners android:radius="5dp" /> <solid android:color="#B6C4F9" /> </shape> </clip> </item> <item android:id="@android:id/progress"> <clip> <shape> <corners android:radius="5dp" /> <solid android:color="#7D95F3" /> </shape> </clip> </item> </layer-list>
thumb.xml 进度条滑块样式
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#40DEF1" /> <size android:width="10dp" android:height="10dp" /> <stroke android:width="1dp" android:color="#40DEF1" /> </shape>
上面两个关于进度条的样式也在activity_main.xml中用到
然后是values下面的styles.xml文件
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/white</item> <item name="colorPrimaryDark">@color/white</item> <item name="colorAccent">@color/colorAccent</item> </style> <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> </resources>
样式和布局讲完了,接下来是工具类:
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 < 0) w = 0; return weekDays[w]; } //将时间戳转化为对应的时间(10位或者13位都可以) public static String formatTime(long time){ String times = null; if(String.valueOf(time).length()>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 { /** * 扫描系统里面的音频文件,返回一个list集合 */ public static List<Song> getMusicData(Context context) { List<Song> list = new ArrayList<Song>(); // 媒体库查询语句(写一个工具类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..."); } /** * Return whether object is empty. * * @param obj The object. * @return {@code true}: yes<br>{@code false}: no */ 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; } /** * Return whether object is not empty. * * @param obj The object. * @return {@code true}: yes<br>{@code false}: no */ 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); } /** * Return whether object1 is equals to object2. * * @param o1 The first object. * @param o2 The second object. * @return {@code true}: yes<br>{@code false}: no */ public static boolean equals(final Object o1, final Object o2) { return o1 == o2 || (o1 != null && o1.equals(o2)); } /** * Require the objects are not null. * * @param objects The object. * @throws NullPointerException if any object is null in objects */ public static void requireNonNull(final Object... objects) { if (objects == null) throw new NullPointerException(); for (Object object : objects) { if (object == null) throw new NullPointerException(); } } /** * Return the nonnull object or default object. * * @param object The object. * @param defaultObject The default object to use with the object is null. * @param <T> The value type. * @return the nonnull object or default object */ public static <T> T getOrDefault(final T object, final T defaultObject) { if (object == null) { return defaultObject; } return object; } /** * Return the hash code of object. * * @param o The object. * @return the hash code of object */ public static int hashCode(final Object o) { return o != null ? o.hashCode() : 0; } }
SPUtils.java 缓存读写工具类,配合Constant使效果更佳
/** * sharepref工具类 */ 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 { /** * 修改状态栏为全透明 * * @param activity */ @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); } } /** * 修改状态栏颜色,支持4.4以上版本 * * @param activity * @param colorId */ 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); } } /** * 状态栏亮色模式,设置状态栏黑色文字、图标, * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android * * @param activity * @return 1:MIUUI 2:Flyme 3:android6.0 */ 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; } /** * 已知系统类型时,设置状态栏黑色文字、图标。 * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android * * @param activity * @param type 1:MIUUI 2:Flyme 3:android6.0 */ 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); } } /** * 状态栏暗色模式,清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标 */ 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); } } /** * 设置状态栏图标为深色和魅族特定的文字风格 * 可以用来判断是否为Flyme用户 * * @param window 需要设置的窗口 * @param dark 是否把状态栏文字及图标颜色设置为深色 * @return boolean 成功执行返回true */ 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; } /** * 需要MIUIV6以上 * * @param activity * @param dark 是否把状态栏文字及图标颜色设置为深色 * @return boolean 成功执行返回true */ 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<Song, BaseViewHolder>{ public MusicListAdapter(int layoutResId, @Nullable List<Song> 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<Song> 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 < 0) { mCurrentPosition = position = mList.size() - 1; Log.e("MainActivity", "mList.size:" + mList.size()); } else 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); } }