diff --git a/app/src/main/assets/litepal.xml b/app/src/main/assets/litepal.xml index f50ac56..954d39e 100644 --- a/app/src/main/assets/litepal.xml +++ b/app/src/main/assets/litepal.xml @@ -1,18 +1,31 @@ + + - + + - + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/adapter/LoveSongAdapter.java b/app/src/main/java/com/example/musicplayer/adapter/LoveSongAdapter.java index 4e7eb81..fc602ca 100644 --- a/app/src/main/java/com/example/musicplayer/adapter/LoveSongAdapter.java +++ b/app/src/main/java/com/example/musicplayer/adapter/LoveSongAdapter.java @@ -1,5 +1,7 @@ package com.example.musicplayer.adapter; +// 导入所需的包和类 + import android.annotation.SuppressLint; import android.content.Context; import android.support.annotation.NonNull; @@ -19,38 +21,47 @@ import com.example.musicplayer.util.FileUtil; import java.util.List; /** + * 用于展示“喜欢的歌曲”列表的适配器。 + *

* Created by 残渊 on 2018/11/30. */ - public class LoveSongAdapter extends RecyclerView.Adapter { - private static final String TAG = "LoveSongAdapter"; - private int footerViewType = 1; - private int itemViewType = 0; - private List mLoveList; - private Context mContext; - private int mLastPosition = -1; - private OnItemClickListener onItemClickListener; + private static final String TAG = "LoveSongAdapter"; // 用于日志输出的标签 + private int footerViewType = 1; // 底部视图类型标识 + private int itemViewType = 0; // 列表项视图类型标识 + private List mLoveList; // 存储喜欢的歌曲数据 + private Context mContext; // 上下文对象 + private int mLastPosition = -1; // 上一次点击的位置 + private OnItemClickListener onItemClickListener; // 点击事件监听器 + /** + * 设置点击事件监听器。 + * @param onItemClickListener 点击事件监听器 + */ public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } + /** + * 构造函数,初始化上下文和喜欢的歌曲列表。 + * @param context 上下文对象 + * @param loveList 喜欢的歌曲列表 + */ public LoveSongAdapter(Context context, List loveList) { mContext = context; mLoveList = loveList; } - - + // ViewHolder,用于缓存视图,提高列表滚动性能 class ViewHolder extends RecyclerView.ViewHolder { - TextView songNameTv; - TextView singerTv; - View playLine; - RippleView item; + TextView songNameTv; // 歌曲名称文本 + TextView singerTv; // 歌手文本 + View playLine; // 播放线条视图 + RippleView item; // 带有水波纹效果的视图 public ViewHolder(View itemView) { super(itemView); - songNameTv = itemView.findViewById(R.id.tv_title); + songNameTv = itemView.findViewById(R.id.tv_title); // 初始化视图 singerTv = itemView.findViewById(R.id.tv_artist); playLine = itemView.findViewById(R.id.line_play); item = itemView.findViewById(R.id.ripple); @@ -58,11 +69,10 @@ public class LoveSongAdapter extends RecyclerView.Adapter { private static final String TAG = "SearchContentAdapter"; - private ArrayList mSongListBeans; - private List mAlbumList; - private static OnItemClickListener mItemClick; - private static OnAlbumItemClickListener mAlbumClick; - private String mSeek; - private Context mContext; - private int mLastPosition = -1; - private int mType; - + private ArrayList mSongListBeans; // 单曲列表数据 + private List mAlbumList; // 专辑列表数据 + private static OnItemClickListener mItemClick; // 单曲点击事件监听器 + private static OnAlbumItemClickListener mAlbumClick; // 专辑点击事件监听器 + private String mSeek; // 搜索关键字 + private Context mContext; // 上下文对象 + private int mLastPosition = -1; // 上一次点击的位置 + private int mType; // 列表类型,单曲或专辑 + // 设置单曲点击事件监听器 public static void setItemClick(OnItemClickListener itemClick) { mItemClick = itemClick; } + // 设置专辑点击事件监听器 public static void setAlbumClick(OnAlbumItemClickListener albumClick) { mAlbumClick = albumClick; } + // 构造函数,初始化专辑搜索适配器 public SearchContentAdapter(List dataBeans, String seek, Context context, int type) { mContext = context; mSeek = seek; @@ -56,7 +60,7 @@ public class SearchContentAdapter extends RecyclerView.Adapter songListBeans, String seek, Context context, int type) { mContext = context; mSeek = seek; @@ -64,10 +68,11 @@ public class SearchContentAdapter extends RecyclerView.Adapter { mItemClick.onClick(position); equalPosition(position); @@ -113,21 +120,24 @@ public class SearchContentAdapter extends RecyclerView.Adapter { mAlbumClick.onClick(position); }); } } - + // 获取列表项的总数 @Override public int getItemCount() { if (mType == Constant.TYPE_SONG) { @@ -138,6 +148,7 @@ public class SearchContentAdapter extends RecyclerView.Adapter { - private List mSearchHistoryList; - private static final int mHistoryType =0; - private static final int mFooterType = 1; - private OnItemClickListener mOnItemClickListener; - private OnDeleteClickListener mOnDeleteClickListener; - private OnFooterClickListener mFooterClickListener; - - public SearchHistoryAdapter(List searchHistoryList){ + private List mSearchHistoryList; // 搜索历史列表数据 + private static final int mHistoryType = 0; // 历史记录项的视图类型 + private static final int mFooterType = 1; // 底部删除按钮的视图类型 + private OnItemClickListener mOnItemClickListener; // 列表项点击事件监听器 + private OnDeleteClickListener mOnDeleteClickListener; // 删除按钮点击事件监听器 + private OnFooterClickListener mFooterClickListener; // 底部删除所有按钮点击事件监听器 + + // 构造函数,初始化搜索历史列表 + public SearchHistoryAdapter(List searchHistoryList) { mSearchHistoryList = searchHistoryList; } + // 创建ViewHolder @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - if(viewType ==mHistoryType){ - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_seek_history_item, - parent,false); + if (viewType == mHistoryType) { + // 历史记录项布局 + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_seek_history_item, parent, false); return new HistoryHolder(view); - }else{ - View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.footer_delete_all_history_item, - parent,false); + } else { + // 底部删除所有按钮布局 + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.footer_delete_all_history_item, parent, false); return new FooterHolder(view); - } } + // 将数据绑定到ViewHolder @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) { - if(holder instanceof HistoryHolder){ - HistoryHolder historyHolder =(HistoryHolder) holder; + if (holder instanceof HistoryHolder) { + HistoryHolder historyHolder = (HistoryHolder) holder; + // 设置历史记录文本 historyHolder.historyTv.setText(mSearchHistoryList.get(position).getHistory()); + // 设置删除按钮点击事件 historyHolder.deleteIv.setOnClickListener(v -> mOnDeleteClickListener.onClick(position)); - + // 设置列表项点击事件 historyHolder.mItemView.setOnRippleCompleteListener(rippleView -> mOnItemClickListener.onClick(position)); - }else{ - FooterHolder footerHolder =(FooterHolder) holder; + } else { + FooterHolder footerHolder = (FooterHolder) holder; + // 设置底部删除所有按钮点击事件 footerHolder.deleteView.setOnClickListener(v -> mFooterClickListener.onClick()); } } + // 获取列表项的总数 @Override public int getItemCount() { - return mSearchHistoryList.size()+1; + return mSearchHistoryList.size() + 1; // 加1是为了显示底部删除所有按钮 } + + // 获取列表项的视图类型 @Override public int getItemViewType(int position) { - return position + 1 == getItemCount() ? mFooterType :mHistoryType; + return position + 1 == getItemCount() ? mFooterType : mHistoryType; } + // 内部类HistoryHolder,用于历史记录列表项 private class HistoryHolder extends RecyclerView.ViewHolder { - TextView historyTv; - ImageView deleteIv; - RippleView mItemView; + TextView historyTv; // 历史记录文本 + ImageView deleteIv; // 删除按钮 + RippleView mItemView; // 列表项背景 public HistoryHolder(View itemView) { super(itemView); @@ -85,8 +95,9 @@ public class SearchHistoryAdapter extends RecyclerView.Adapter { - private static final String TAG = "SongAdapter"; - private int footerViewType = 1; - private int itemViewType = 0; - private List mMp3InfoList; - private Context mContext; - private int mLastPosition = -1; - private OnItemClickListener onItemClickListener; - + private static final String TAG = "SongAdapter"; // 用于日志输出的标签 + private int footerViewType = 1; // 底部视图类型标识 + private int itemViewType = 0; // 列表项视图类型标识 + private List mMp3InfoList; // 本地歌曲列表数据 + private Context mContext; // 上下文对象 + private int mLastPosition = -1; // 上一次点击的位置 + private OnItemClickListener onItemClickListener; // 点击事件监听器 + + // 设置点击事件监听器 public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } + // 构造函数,初始化上下文和本地歌曲列表 public SongAdapter(Context context, List mp3InfoList) { mContext = context; mMp3InfoList = mp3InfoList; } - + // 内部类ViewHolder,用于歌曲列表项 static class ViewHolder extends RecyclerView.ViewHolder { - TextView songNameTv; - TextView artistTv; - ImageView playingIv; - RippleView songView; + TextView songNameTv; // 歌曲名称文本 + TextView artistTv; // 歌手文本 + ImageView playingIv; // 播放状态图标 + RippleView songView; // 歌曲项背景 public ViewHolder(View itemView) { super(itemView); @@ -58,12 +62,9 @@ public class SongAdapter extends RecyclerView.Adapter { } } - /** - * 底部holder - */ + // 底部holder static class FooterHolder extends RecyclerView.ViewHolder { - - TextView numTv; + TextView numTv; // 底部信息文本 public FooterHolder(View itemView) { super(itemView); @@ -71,6 +72,7 @@ public class SongAdapter extends RecyclerView.Adapter { } } + // 创建ViewHolder @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { if (viewType == itemViewType) { @@ -86,6 +88,7 @@ public class SongAdapter extends RecyclerView.Adapter { } } + // 将数据绑定到ViewHolder @SuppressLint("SetTextI18n") @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) { @@ -95,20 +98,20 @@ public class SongAdapter extends RecyclerView.Adapter { holder.songNameTv.setText(mp3Info.getName()); holder.artistTv.setText(mp3Info.getSinger()); - //根据播放的歌曲是否为当前列表的歌曲显示 + // 根据播放的歌曲是否为当前列表的歌曲显示 String songId = Objects.requireNonNull(FileUtil.getSong()).getSongId(); - if(songId!=null&&(mp3Info.getSongId().equals(songId))){ - holder.songNameTv.setTextColor(App.getContext(). - getResources().getColor(R.color.musicStyle_low)); - holder.artistTv.setTextColor(App.getContext(). - getResources().getColor(R.color.musicStyle_low)); + if (songId != null && mp3Info.getSongId().equals(songId)) { + holder.songNameTv.setTextColor(App.getContext() + .getResources().getColor(R.color.musicStyle_low)); + holder.artistTv.setTextColor(App.getContext() + .getResources().getColor(R.color.musicStyle_low)); holder.playingIv.setVisibility(View.VISIBLE); mLastPosition = position; - }else { - holder.songNameTv.setTextColor(App.getContext(). - getResources().getColor(R.color.white)); - holder.artistTv.setTextColor(App.getContext(). - getResources().getColor(R.color.white)); + } else { + holder.songNameTv.setTextColor(App.getContext() + .getResources().getColor(R.color.white)); + holder.artistTv.setTextColor(App.getContext() + .getResources().getColor(R.color.white)); holder.playingIv.setVisibility(View.GONE); } holder.songView.setOnRippleCompleteListener(rippleView -> { @@ -117,11 +120,11 @@ public class SongAdapter extends RecyclerView.Adapter { }); } else { FooterHolder footerHolder = (FooterHolder) viewHolder; - footerHolder.numTv.setText("共" +mMp3InfoList.size()+ "首音乐"); + footerHolder.numTv.setText("共" + mMp3InfoList.size() + "首音乐"); } } - //判断点击的是否为上一个点击的项目 + // 判断点击的是否为上一个点击的项目 public void equalPosition(int position) { if (position != mLastPosition) { notifyItemChanged(mLastPosition); @@ -130,17 +133,15 @@ public class SongAdapter extends RecyclerView.Adapter { notifyItemChanged(position); } + // 获取列表项的总数 @Override public int getItemCount() { - return mMp3InfoList.size() + 1; + return mMp3InfoList.size() + 1; // 加1是为了显示底部视图 } + // 获取列表项的视图类型 @Override public int getItemViewType(int position) { return position + 1 == getItemCount() ? footerViewType : itemViewType; } - - - - -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/adapter/TabAdapter.java b/app/src/main/java/com/example/musicplayer/adapter/TabAdapter.java index 2bc27d9..4922390 100644 --- a/app/src/main/java/com/example/musicplayer/adapter/TabAdapter.java +++ b/app/src/main/java/com/example/musicplayer/adapter/TabAdapter.java @@ -1,5 +1,6 @@ package com.example.musicplayer.adapter; +// 导入所需的包和类 import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; @@ -7,31 +8,35 @@ import android.support.v4.app.FragmentPagerAdapter; import java.util.List; /** + * 顶部导航栏的适配器,用于为每个标签页提供对应的 Fragment。 * Created by 残渊 on 2018/11/25. */ - public class TabAdapter extends FragmentPagerAdapter { - private List mFragmentList;//顶部导航栏的内容即fragment - private List mTitle;//顶部导航栏的标题 - + private List mFragmentList; // 顶部导航栏的内容,即 Fragment 列表 + private List mTitle; // 顶部导航栏的标题列表 - public TabAdapter(FragmentManager fragmentManager, Listfragments, Listtitle){ + // 构造函数,初始化 FragmentManager、Fragment 列表和标题列表 + public TabAdapter(FragmentManager fragmentManager, List fragments, List title) { super(fragmentManager); - mFragmentList=fragments; - mTitle=title; - + mFragmentList = fragments; + mTitle = title; } + + // 获取指定位置的 Fragment @Override public Fragment getItem(int position) { return mFragmentList.get(position); } + // 获取 Fragment 的总数 @Override public int getCount() { return mFragmentList.size(); } + + // 获取指定位置的标题 @Override public CharSequence getPageTitle(int position) { return mTitle.get(position); } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/app/Api.java b/app/src/main/java/com/example/musicplayer/app/Api.java index 802817a..79a43d7 100644 --- a/app/src/main/java/com/example/musicplayer/app/Api.java +++ b/app/src/main/java/com/example/musicplayer/app/Api.java @@ -3,42 +3,41 @@ package com.example.musicplayer.app; import android.os.Environment; /** + * 提供音乐播放器应用中使用的各类API接口地址和常量。 * Created by 残渊 on 2018/10/26. */ - public class Api { - //根据歌手获取歌手图片的baseUrl - public static final String SINGER_PIC_BASE_URL ="http://music.163.com/"; - public static final String SINGER_PIC="api/search/get/web?csrf_token=&type=100"; - //获取歌手图片需要添加user-agent的表头 - public static final String HEADER_USER_AGENT="User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"; + // 根据歌手获取歌手图片的baseUrl + public static final String SINGER_PIC_BASE_URL = "http://music.163.com/"; + public static final String SINGER_PIC = "api/search/get/web?csrf_token=&type=100"; + // 获取歌手图片需要添加user-agent的表头 + public static final String HEADER_USER_AGENT = "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36"; /** - * 存取到手机内存的文件名 + * 存储到手机内存的文件路径 */ - public static String STORAGE_IMG_FILE= App.getContext().getExternalFilesDir("") + "/yuanmusic/img/"; - public static String STORAGE_LRC_FILE= App.getContext().getExternalFilesDir("") + "/yuanmusic/lrc/"; - public static String STORAGE_SONG_FILE= Environment.getExternalStorageDirectory() + "/Sxmusic/download/"; - - //Fiddler抓包qq音乐网站后的地址 - public static final String FIDDLER_BASE_QQ_URL ="https://c.y.qq.com/"; - public static final String FIDDLER_BASE_SONG_URL="https://u.y.qq.com/"; //获取播放地址的baseUrl - //搜索功能 - public static final String SEARCH_SONG ="soso/fcgi-bin/client_search_cp?n=30&format=json"; //歌曲,n为一页30首 - public static final String SEARCH_ALBUM="soso/fcgi-bin/client_search_cp?n=20&format=json&t=8";//专辑,n为一页20张 - //得到歌曲的播放地址 - public static final String SONG_URL="cgi-bin/musicu.fcg?format=json"; - public static final String SONG_URL_DATA_LEFT="%7B%22req_0%22%3A%7B%22module%22%3A%22vkey.GetVkeyServer%22%2C%22method%22%3A%22CgiGetVkey%22%2C%22param%22%3A%7B%22guid%22%3A%22358840384%22%2C%22songmid%22%3A%5B%22"; - public static final String SONG_URL_DATA_RIGHT="%22%5D%2C%22songtype%22%3A%5B0%5D%2C%22uin%22%3A%221443481947%22%2C%22loginflag%22%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A%221443481947%22%2C%22format%22%3A%22json%22%2C%22ct%22%3A24%2C%22cv%22%3A0%7D%7D"; - public static final String MP4 =".mp4"; - //专辑照片 - public static final String ALBUM_PIC="http://y.gtimg.cn/music/photo_new/T002R180x180M000"; - public static final String JPG=".jpg"; - //专辑详细 - public static final String ALBUM_DETAIL="v8/fcg-bin/fcg_v8_album_info_cp.fcg?format=json"; - //歌词 - public static final String SONG_LRC ="soso/fcgi-bin/client_search_cp?p=1&n=30&format=json&t=7";//搜索歌词 - public static final String ONLINE_SONG_LRC="https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?format=json&nobase64=1";//根据qq音乐的mid获取歌词 - public static final String HEADER_REFERER="Referer:https://y.qq.com/portal/player.html";//得到歌词需要添加Referer的表头 + public static String STORAGE_IMG_FILE = App.getContext().getExternalFilesDir("") + "/yuanmusic/img/"; + public static String STORAGE_LRC_FILE = App.getContext().getExternalFilesDir("") + "/yuanmusic/lrc/"; + public static String STORAGE_SONG_FILE = Environment.getExternalStorageDirectory() + "/Sxmusic/download/"; -} + // Fiddler抓包qq音乐网站后的地址 + public static final String FIDDLER_BASE_QQ_URL = "https://c.y.qq.com/"; + public static final String FIDDLER_BASE_SONG_URL = "https://u.y.qq.com/"; // 获取播放地址的baseUrl + // 搜索功能 + public static final String SEARCH_SONG = "soso/fcgi-bin/client_search_cp?n=30&format=json"; // 歌曲,n为一页30首 + public static final String SEARCH_ALBUM = "soso/fcgi-bin/client_search_cp?n=20&format=json&t=8"; // 专辑,n为一页20张 + // 得到歌曲的播放地址 + public static final String SONG_URL = "cgi-bin/musicu.fcg?format=json"; + public static final String SONG_URL_DATA_LEFT = "%7B%22req_0%22%3A%7B%22module%22%3A%22vkey.GetVkeyServer%22%2C%22method%22%3A%22CgiGetVkey%22%2C%22param%22%3A%7B%22guid%22%3A%22358840384%22%2C%22songmid%22%3A%5B%22"; + public static final String SONG_URL_DATA_RIGHT = "%22%5D%2C%22songtype%22%3A%5B0%5D%2C%22uin%22%3A%221443481947%22%2C%22loginflag%22%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A%221443481947%22%2C%22format%22%3A%22json%22%2C%22ct%22%3A24%2C%22cv%22%3A0%7D%7D"; + public static final String MP4 = ".mp4"; + // 专辑照片 + public static final String ALBUM_PIC = "http://y.gtimg.cn/music/photo_new/T002R180x180M000"; + public static final String JPG = ".jpg"; + // 专辑详细 + public static final String ALBUM_DETAIL = "v8/fcg-bin/fcg_v8_album_info_cp.fcg?format=json"; + // 歌词 + public static final String SONG_LRC = "soso/fcgi-bin/client_search_cp?p=1&n=30&format=json&t=7"; // 搜索歌词 + public static final String ONLINE_SONG_LRC = "https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?format=json&nobase64=1"; // 根据qq音乐的mid获取歌词 + public static final String HEADER_REFERER = "Referer:https://y.qq.com/portal/player.html"; // 得到歌词需要添加Referer的表头 +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/app/App.java b/app/src/main/java/com/example/musicplayer/app/App.java index 275a7d8..df87203 100644 --- a/app/src/main/java/com/example/musicplayer/app/App.java +++ b/app/src/main/java/com/example/musicplayer/app/App.java @@ -1,5 +1,6 @@ package com.example.musicplayer.app; +// 导入所需的包和类 import android.annotation.SuppressLint; import android.app.Application; import android.content.Context; @@ -7,22 +8,30 @@ import android.content.Context; import org.litepal.LitePal; /** - * 获取全局Context + * 应用全局上下文类,用于获取全局Context实例。 + *

* Created by 残渊 on 2018/7/17. */ - public class App extends Application { + // 使用SuppressLint注解来抑制静态字段可能引起的内存泄漏警告 @SuppressLint("StaticFieldLeak") - private static Context context; + private static Context context; // 静态变量,用于存储全局Context对象 + /** + * 在应用创建时初始化全局Context和LitePal数据库。 + */ @Override - public void onCreate(){ - super.onCreate(); - context=getApplicationContext(); - LitePal.initialize(this); + public void onCreate() { + super.onCreate(); // 调用父类的onCreate方法 + context = getApplicationContext(); // 初始化全局Context对象 + LitePal.initialize(this); // 初始化LitePal数据库 } - public static Context getContext(){ + /** + * 提供全局Context对象的获取方法。 + * @return 返回全局Context对象 + */ + public static Context getContext() { return context; } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/app/Constant.java b/app/src/main/java/com/example/musicplayer/app/Constant.java index 2c7b0db..e0cfaf0 100644 --- a/app/src/main/java/com/example/musicplayer/app/Constant.java +++ b/app/src/main/java/com/example/musicplayer/app/Constant.java @@ -1,82 +1,82 @@ package com.example.musicplayer.app; /** + * 定义应用中使用的常量类。 * Created by 残渊 on 2018/11/23. */ - public class Constant { + // 日志标签 public static final String TAG = "jsyjst"; + // 分页加载的偏移量 public static final int OFFSET = 30; + // 类型常量,用于区分歌曲和专辑 public static final int TYPE_SONG = 1; public static final int TYPE_ALBUM = 2; - //播放列表 - public static final int LIST_TYPE_LOCAL = 1; //本地列表 - public static final int LIST_TYPE_ONLINE = 2; //专辑列表 - public static final int LIST_TYPE_LOVE = 3; //我的收藏列表 - public static final int LIST_TYPE_HISTORY = 4; //最近播放列表 - public static final int LIST_TYPE_DOWNLOAD = 5; //下载列表 + // 播放列表类型 + public static final int LIST_TYPE_LOCAL = 1; // 本地列表 + public static final int LIST_TYPE_ONLINE = 2; // 专辑列表 + public static final int LIST_TYPE_LOVE = 3; // 我的收藏列表 + public static final int LIST_TYPE_HISTORY = 4; // 最近播放列表 + public static final int LIST_TYPE_DOWNLOAD = 5; // 下载列表 + // 搜索历史最大条目数 public static final int HISTORY_MAX_SIZE = 100; - - //布局 + // 布局状态常量 public static final int NORMAL_STATE = 0; public static final int LOADING_STATE = 1; public static final int ERROR_STATE = 2; - //playerStatus + // 播放器状态常量 public static final String PLAYER_STATUS = "PlayerStatus"; public static final int SONG_PLAY = 0; public static final int SONG_PAUSE = 1; public static final int SONG_RESUME = 2; public static final int SONG_CHANGE = 3; - - //KEY + // 键值对常量 public static final String ALBUM_ID_KEY = "id"; public static final String ALBUM_NAME_KEY = "albumName"; public static final String SINGER_NAME_KEY = "singerName"; public static final String ALBUM_PIC_KEY = "albumPic"; public static final String PUBLIC_TIME_KEY = "publicTime"; - - //TAG + // 错误标签 public static final String TAG_ERROR = "error"; - //网络与非网络歌曲 + // 网络与非网络歌曲标识 public static final int SONG_ONLINE = 0; public static final int SONG_LOCAL = 1; - - //后缀 + // 后缀常量 public static final String LRC = ".lrc"; - //音乐id + // 音乐id未找到标识 public static final String SONG_ID_UNFIND = "unFind"; - //播放顺序 - public static final int PLAY_ORDER = 0;//顺序播放 - public static final int PLAY_SINGLE = 1;//单曲循环 - public static final int PLAY_RANDOM = 2;//随机播放 + // 播放顺序常量 + public static final int PLAY_ORDER = 0; // 顺序播放 + public static final int PLAY_SINGLE = 1; // 单曲循环 + public static final int PLAY_RANDOM = 2; // 随机播放 - //Preferences + // Preferences常量 public static final String SHARED_PREFERENCES_NAME = "prefs"; - public static final String PREFS_PLAY_MODE = "play_mode";//播放状态 + public static final String PREFS_PLAY_MODE = "play_mode"; // 播放模式 - //download + // 下载状态常量 public final static int TYPE_DOWNLOADING = 0; public final static int TYPE_DOWNLOAD_PAUSED = 1; public final static int TYPE_DOWNLOAD_CANCELED = 2; public final static int TYPE_DOWNLOAD_SUCCESS = 3; public final static int TYPE_DOWNLOAD_FAILED = 4; public final static int TYPE_DOWNLOADED = 5; - public final static int TYPE_DOWNLOAD_ADD=6; + public final static int TYPE_DOWNLOAD_ADD = 6; - //正在下载歌曲列表的状态 + // 正在下载歌曲列表的状态常量 public final static int DOWNLOAD_PAUSED = 0; - public final static int DOWNLOAD_WAIT=1; - public final static int DOWNLOAD_ING=2; - public final static int DOWNLOAD_READY=3; -} + public final static int DOWNLOAD_WAIT = 1; + public final static int DOWNLOAD_ING = 2; + public final static int DOWNLOAD_READY = 3; +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/activity/BaseActivity.java b/app/src/main/java/com/example/musicplayer/base/activity/BaseActivity.java index 9e80550..5a18314 100644 --- a/app/src/main/java/com/example/musicplayer/base/activity/BaseActivity.java +++ b/app/src/main/java/com/example/musicplayer/base/activity/BaseActivity.java @@ -1,5 +1,6 @@ package com.example.musicplayer.base.activity; +// 导入所需的包和类 import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; @@ -12,63 +13,68 @@ import butterknife.ButterKnife; import butterknife.Unbinder; /** - *

- *     author : 残渊
- *     time   : 2019/07/17
- *     desc   : 所有活动的基类
- * 
+ * 所有活动的基类,提供了基本的初始化操作和视图绑定。 + *

+ * author : 残渊 + * time : 2019/07/17 + * desc : 所有活动的基类 */ - public abstract class BaseActivity extends AppCompatActivity implements BaseView { - private Unbinder mBinder; + private Unbinder mBinder; // ButterKnife绑定对象,用于解绑 - protected abstract int getLayoutId(); //获取布局id - protected abstract void initView(); //初始化布局 - protected abstract void initData(); //初始化数据 - protected abstract void onClick();//点击事件 + // 获取布局id + protected abstract int getLayoutId(); + // 初始化布局 + protected abstract void initView(); + // 初始化数据 + protected abstract void initData(); + // 点击事件 + protected abstract void onClick(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(getLayoutId()); - ButterKnife.bind(this); - initView(); - initData(); - onClick(); + setContentView(getLayoutId()); // 设置布局 + ButterKnife.bind(this); // 绑定视图 + initView(); // 初始化视图 + initData(); // 初始化数据 + onClick(); // 点击事件 } @Override protected void onDestroy() { super.onDestroy(); + // 解绑ButterKnife,避免内存泄漏 if(mBinder != null && mBinder != mBinder.EMPTY){ mBinder.unbind(); mBinder = null; } } + // 显示提示信息 @Override public void showToast(String message) { - CommonUtil.showToast(this,message); + CommonUtil.showToast(this, message); } + // 显示正常视图 @Override public void showNormalView() { - } + // 显示错误视图 @Override public void showErrorView() { - } + // 显示加载视图 @Override public void showLoading() { - } + // 重新加载数据 @Override public void reload() { - } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/activity/BaseMvpActivity.java b/app/src/main/java/com/example/musicplayer/base/activity/BaseMvpActivity.java index 3ad7f75..b99e694 100644 --- a/app/src/main/java/com/example/musicplayer/base/activity/BaseMvpActivity.java +++ b/app/src/main/java/com/example/musicplayer/base/activity/BaseMvpActivity.java @@ -3,29 +3,36 @@ package com.example.musicplayer.base.activity; import com.example.musicplayer.base.presenter.IPresenter; /** - *

- *     author : 残渊
- *     time   : 2019/07/17
- *     desc   :
- * 
+ * MVP架构中的基Activity类,用于所有采用MVP模式的Activity。 + *

+ * author : 残渊 + * time : 2019/07/17 + * desc : 提供了Presenter的绑定和解绑操作。 */ - -public abstract class BaseMvpActivity extends BaseActivity{ +public abstract class BaseMvpActivity extends BaseActivity { + // 抽象方法,用于获取具体的Presenter实例 protected abstract T getPresenter(); + // Presenter成员变量,用于与View进行交互 protected T mPresenter; + /** + * 初始化视图,包括绑定Presenter。 + */ @Override protected void initView() { - mPresenter = getPresenter(); - mPresenter.attachView(this); + mPresenter = getPresenter(); // 获取Presenter实例 + mPresenter.attachView(this); // 将当前Activity绑定到Presenter的View } + /** + * 在Activity销毁时解绑Presenter,并置空引用。 + */ @Override protected void onDestroy() { - super.onDestroy(); - if(mPresenter != null){ - mPresenter.detachView(); - mPresenter = null; + super.onDestroy(); // 调用父类的onDestroy方法 + if (mPresenter != null) { + mPresenter.detachView(); // 从Presenter解绑当前Activity的View + mPresenter = null; // 置空引用,避免内存泄漏 } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/fragment/BaseFragment.java b/app/src/main/java/com/example/musicplayer/base/fragment/BaseFragment.java index 62b5064..d4d170b 100644 --- a/app/src/main/java/com/example/musicplayer/base/fragment/BaseFragment.java +++ b/app/src/main/java/com/example/musicplayer/base/fragment/BaseFragment.java @@ -1,5 +1,6 @@ package com.example.musicplayer.base.fragment; +// 导入所需的包和类 import android.app.Activity; import android.content.Context; import android.os.Bundle; @@ -16,75 +17,92 @@ import butterknife.ButterKnife; import butterknife.Unbinder; /** - *

- *     author : 残渊
- *     time   : 2019/07/14
- *     desc   :
- * 
+ * 所有Fragment的基类,提供了基本的初始化操作和视图绑定。 + *

+ * author : 残渊 + * time : 2019/07/14 + * desc : 基Fragment类,用于简化Fragment的创建和视图管理。 */ - public abstract class BaseFragment extends BaseLazyFragment implements BaseView { - private Unbinder mBinder; - protected Activity mActivity; - protected abstract void initView(); //初始化控件 - protected abstract void loadData(); //加载数据 - protected abstract int getLayoutId(); //获取Fragment的布局id - + private Unbinder mBinder; // ButterKnife绑定对象,用于解绑 + protected Activity mActivity; // 引用Activity,方便调用Activity的方法 + + // 初始化控件的抽象方法 + protected abstract void initView(); + // 加载数据的抽象方法 + protected abstract void loadData(); + // 获取Fragment的布局id的抽象方法 + protected abstract int getLayoutId(); + + /** + * 当Fragment附加到Activity时调用,保存Activity的引用。 + * @param context 上下文对象 + */ @Override public void onAttach(Context context) { super.onAttach(context); mActivity = (Activity) context; } + /** + * 创建视图,通过LayoutInflater加载布局,并使用ButterKnife进行视图绑定。 + * @param inflater 布局填充器 + * @param container 容器 + * @param savedInstanceState 保存的实例状态 + * @return 返回Fragment的视图 + */ @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View view = inflater.inflate(getLayoutId(),container,false); - mBinder = ButterKnife.bind(this,view); + View view = inflater.inflate(getLayoutId(), container, false); + mBinder = ButterKnife.bind(this, view); initView(); return view; } - + /** + * 当视图销毁时解绑ButterKnife,避免内存泄漏。 + */ @Override public void onDestroyView() { super.onDestroyView(); - if(mBinder != null && mBinder != Unbinder.EMPTY){ + if (mBinder != null && mBinder != Unbinder.EMPTY) { mBinder.unbind(); mBinder = null; } } - + /** + * 懒加载数据,仅当Fragment对用户可见时加载数据。 + */ @Override protected void lazyLoadData() { loadData(); } + // 实现BaseView接口的方法,但未具体实现,子类可根据需要实现 @Override public void showNormalView() { - } @Override public void showErrorView() { - } @Override public void showLoading() { - } @Override public void reload() { - } + /** + * 显示提示信息,使用CommonUtil工具类简化提示信息的显示。 + * @param message 提示信息 + */ @Override public void showToast(String message) { - CommonUtil.showToast(mActivity,message); + CommonUtil.showToast(mActivity, message); } - - -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/fragment/BaseLazyFragment.java b/app/src/main/java/com/example/musicplayer/base/fragment/BaseLazyFragment.java index db4afd3..0b1c49b 100644 --- a/app/src/main/java/com/example/musicplayer/base/fragment/BaseLazyFragment.java +++ b/app/src/main/java/com/example/musicplayer/base/fragment/BaseLazyFragment.java @@ -1,5 +1,6 @@ package com.example.musicplayer.base.fragment; +// 导入所需的包和类 import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -8,74 +9,95 @@ import android.util.Log; import android.view.View; /** - *

- *     author : 残渊
- *     time   : 2019/07/14
- *     desc   : 实现懒加载的Fragment
- * 
+ * 实现懒加载机制的Fragment基类。 + *

+ * author : 残渊 + * time : 2019/07/14 + * desc : 提供懒加载数据的逻辑,确保数据只在Fragment对用户可见时加载。 */ - public abstract class BaseLazyFragment extends Fragment { - private boolean isViewCreated = false;//布局是否被创建 - private boolean isLoadData = false;//数据是否加载 - private boolean isFirstVisible = true;//是否第一次可见 - private static final String TAG = "BaseLazyFragment"; + private boolean isViewCreated = false; // 标记布局是否已被创建 + private boolean isLoadData = false; // 标记数据是否已加载 + private boolean isFirstVisible = true; // 标记是否是第一次可见 + private static final String TAG = "BaseLazyFragment"; // 日志标签 - protected abstract void lazyLoadData(); //加载数据 + // 抽象方法,用于子类实现具体的懒加载数据逻辑 + protected abstract void lazyLoadData(); + /** + * 当Fragment的视图被创建后调用,标记布局已被创建。 + * @param view 视图 + * @param savedInstanceState 保存的实例状态 + */ @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); isViewCreated = true; } + /** + * 当Fragment的活动被创建后调用,如果Fragment可见且数据未加载,则加载数据。 + * @param savedInstanceState 保存的实例状态 + */ @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - if(isFragmentVisible(this)){ + if (isFragmentVisible(this)) { if (this.getParentFragment() == null || isFragmentVisible(this.getParentFragment())) { Log.d(TAG, "onActivityCreated: 加载数据"); lazyLoadData(); isLoadData = true; - if(isFirstVisible) isFirstVisible = false; + if (isFirstVisible) isFirstVisible = false; } } } - //在使用ViewPage时,加载数据 + /** + * 当Fragment的可见性发生变化时调用,如果Fragment可见且数据未加载且布局已创建,则加载数据。 + * @param isVisibleToUser 是否对用户可见 + */ @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); - if(isFragmentVisible(this)&& !isLoadData && isViewCreated){ + if (isFragmentVisible(this) && !isLoadData && isViewCreated) { lazyLoadData(); isLoadData = true; } } - - //调用show方法时加载数据 + /** + * 当Fragment的隐藏状态发生变化时调用,如果Fragment不再隐藏且数据未加载且是第一次可见,则加载数据。 + * @param hidden 是否被隐藏 + */ @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); - //onHiddenChanged调用在Resumed之前,所以此时可能fragment被add, 但还没调用show方法 - if(!hidden && !this.isResumed()) + if (!hidden && !this.isResumed()) return; - //使用hide和show时,fragment的所有生命周期方法都不会调用,除了onHiddenChanged() - if(!hidden && isFirstVisible){ + if (!hidden && isFirstVisible) { Log.d(TAG, "onHiddenChanged: 加载数据"); lazyLoadData(); isFirstVisible = false; } } + /** + * 判断Fragment是否可见。 + * @param fragment Fragment对象 + * @return 是否可见 + */ private boolean isFragmentVisible(Fragment fragment){ - return fragment.getUserVisibleHint()&&!fragment.isHidden(); + return fragment.getUserVisibleHint() && !fragment.isHidden(); } + /** + * 当视图销毁时重置标记。 + */ @Override public void onDestroyView() { super.onDestroyView(); isFirstVisible = true; isLoadData = false; - isViewCreated =false; + isViewCreated = false; } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/fragment/BaseLoadingFragment.java b/app/src/main/java/com/example/musicplayer/base/fragment/BaseLoadingFragment.java index cc22c91..e2633a0 100644 --- a/app/src/main/java/com/example/musicplayer/base/fragment/BaseLoadingFragment.java +++ b/app/src/main/java/com/example/musicplayer/base/fragment/BaseLoadingFragment.java @@ -1,5 +1,6 @@ package com.example.musicplayer.base.fragment; +// 导入所需的包和类 import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -16,100 +17,120 @@ import static com.example.musicplayer.app.Constant.LOADING_STATE; import static com.example.musicplayer.app.Constant.NORMAL_STATE; /** - *

- *     author : 残渊
- *     time   : 2019/07/15
- *     desc   :
- * 
+ * 带有加载、错误和正常状态视图的MVP基Fragment类。 + *

+ * author : 残渊 + * time : 2019/07/15 + * desc : 提供了三种状态视图的切换逻辑,包括加载中、错误和正常显示。 */ - public abstract class BaseLoadingFragment extends BaseMvpFragment { - private View mNormalView; //正常布局 - private View mErrorView; //错误布局 - private View mLoadingView;//加载布局 - private AVLoadingIndicatorView avLoadingView; - - private int mCurrentState = NORMAL_STATE;//当前布局状态 - + private View mNormalView; // 正常布局 + private View mErrorView; // 错误布局 + private View mLoadingView; // 加载布局 + private AVLoadingIndicatorView avLoadingView; // 加载动画视图 + + private int mCurrentState = NORMAL_STATE; // 当前视图状态 + + /** + * 当Fragment的视图创建完成后调用,初始化三种状态的视图。 + * @param view 视图 + * @param savedInstanceState 保存的实例状态 + */ @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - if(getView() == null) return; + if (getView() == null) return; mNormalView = view.findViewById(R.id.normalView); - if(mNormalView == null){ + if (mNormalView == null) { throw new IllegalStateException("The subclass of BaseLoadFragment must contain a View it's id is named normal_view"); } - if(!(mNormalView.getParent() instanceof ViewGroup)){ + if (!(mNormalView.getParent() instanceof ViewGroup)) { throw new IllegalStateException("mNormalView's parentView should be a ViewGroup"); } ViewGroup parentPanel = (ViewGroup) mNormalView.getParent(); - View.inflate(mActivity,R.layout.error_view,parentPanel); //加载错误布局 - View.inflate(mActivity,R.layout.loading_view,parentPanel);//加载loading布局 + View.inflate(mActivity, R.layout.error_view, parentPanel); // 加载错误布局 + View.inflate(mActivity, R.layout.loading_view, parentPanel); // 加载加载布局 mLoadingView = parentPanel.findViewById(R.id.loadingView); avLoadingView = parentPanel.findViewById(R.id.avLoading); mErrorView = parentPanel.findViewById(R.id.errorView); TextView reloadBtn = parentPanel.findViewById(R.id.reloadBtn); - reloadBtn.setOnClickListener(view1 -> reload()); //重新加载 + reloadBtn.setOnClickListener(view1 -> reload()); // 设置重新加载按钮的点击事件 mNormalView.setVisibility(View.VISIBLE); mErrorView.setVisibility(View.GONE); mLoadingView.setVisibility(View.GONE); } + /** + * 显示正常视图,并隐藏其他视图。 + */ @Override public void showNormalView() { super.showNormalView(); - if(mCurrentState == NORMAL_STATE) return; + if (mCurrentState == NORMAL_STATE) return; hideViewByState(mCurrentState); mCurrentState = NORMAL_STATE; showViewByState(mCurrentState); } + /** + * 显示错误视图,并隐藏其他视图。 + */ @Override public void showErrorView() { super.showErrorView(); - if(mCurrentState == ERROR_STATE) return; + if (mCurrentState == ERROR_STATE) return; hideViewByState(mCurrentState); mCurrentState = ERROR_STATE; showViewByState(mCurrentState); } + /** + * 显示加载视图,并隐藏其他视图。 + */ @Override public void showLoading() { super.showLoading(); - if(mCurrentState == LOADING_STATE) return; + if (mCurrentState == LOADING_STATE) return; hideViewByState(mCurrentState); mCurrentState = LOADING_STATE; showViewByState(mCurrentState); } - private void hideViewByState(int state){ - if(state == NORMAL_STATE){ - if(mNormalView == null) return; + /** + * 根据状态隐藏视图。 + * @param state 视图状态 + */ + private void hideViewByState(int state) { + if (state == NORMAL_STATE) { + if (mNormalView == null) return; mNormalView.setVisibility(View.GONE); - }else if(state == LOADING_STATE){ - if(mLoadingView == null||avLoadingView == null) return; - mLoadingView.setVisibility(View.GONE); - }else { - if(mErrorView == null ) return; + } else if (state == LOADING_STATE) { + if (mLoadingView == null || avLoadingView == null) return; + mLoadingView.setVisibility(View.GONE); + } else { + if (mErrorView == null) return; mErrorView.setVisibility(View.GONE); } } - private void showViewByState(int state){ - if(state == NORMAL_STATE){ - if(mNormalView == null) return; + + /** + * 根据状态显示视图。 + * @param state 视图状态 + */ + private void showViewByState(int state) { + if (state == NORMAL_STATE) { + if (mNormalView == null) return; mNormalView.setVisibility(View.VISIBLE); - }else if(state == LOADING_STATE){ - if(mLoadingView == null||avLoadingView == null) return; + } else if (state == LOADING_STATE) { + if (mLoadingView == null || avLoadingView == null) return; mLoadingView.setVisibility(View.VISIBLE); - avLoadingView.show(); - }else { - if(mErrorView == null ) return; + avLoadingView.show(); // 显示加载动画 + } else { + if (mErrorView == null) return; mErrorView.setVisibility(View.VISIBLE); } } - - -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/fragment/BaseMvpFragment.java b/app/src/main/java/com/example/musicplayer/base/fragment/BaseMvpFragment.java index 6eeda85..483da10 100644 --- a/app/src/main/java/com/example/musicplayer/base/fragment/BaseMvpFragment.java +++ b/app/src/main/java/com/example/musicplayer/base/fragment/BaseMvpFragment.java @@ -1,31 +1,39 @@ package com.example.musicplayer.base.fragment; +// 导入所需的包和类 import com.example.musicplayer.base.presenter.IPresenter; /** - *

- *     author : 残渊
- *     time   : 2019/07/14
- *     desc   : Mvp模式Fragment的基类
- * 
+ * MVP模式下Fragment的基类。 + *

+ * author : 残渊 + * time : 2019/07/14 + * desc : 提供了Presenter的绑定和解绑操作,用于MVP架构的Fragment。 */ - public abstract class BaseMvpFragment extends BaseFragment { + // 抽象方法,用于获取具体的Presenter实例 protected abstract T getPresenter(); + // Presenter成员变量,用于与View进行交互 protected T mPresenter; + /** + * 初始化视图,包括绑定Presenter。 + */ @Override protected void initView() { - mPresenter = getPresenter(); - mPresenter.attachView(this); + mPresenter = getPresenter(); // 获取Presenter实例 + mPresenter.attachView(this); // 将当前Fragment绑定到Presenter的View } + /** + * 在Fragment销毁时解绑Presenter,并置空引用。 + */ @Override public void onDestroy() { - if(mPresenter != null){ - mPresenter.detachView(); - mPresenter = null; + if (mPresenter != null) { + mPresenter.detachView(); // 从Presenter解绑当前Fragment的View + mPresenter = null; // 置空引用,避免内存泄漏 } - super.onDestroy(); + super.onDestroy(); // 调用父类的onDestroy方法 } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/observer/BaseObserver.java b/app/src/main/java/com/example/musicplayer/base/observer/BaseObserver.java index c2d7082..bac4c72 100644 --- a/app/src/main/java/com/example/musicplayer/base/observer/BaseObserver.java +++ b/app/src/main/java/com/example/musicplayer/base/observer/BaseObserver.java @@ -1,5 +1,6 @@ package com.example.musicplayer.base.observer; +// 导入所需的包和类 import android.net.ParseException; import android.os.Handler; import android.util.Log; @@ -19,57 +20,69 @@ import retrofit2.HttpException; import static com.example.musicplayer.app.Constant.TAG_ERROR; /** - *

- *     author : 残渊
- *     time   : 2019/07/16
- *     desc   : 对RxJava的下游即数据进行处理
- * 
+ * 对RxJava的下游即数据进行处理的基类。 + *

+ * author : 残渊 + * time : 2019/07/16 + * desc : 处理网络请求的响应,包括成功、失败和各种异常情况。 */ - public class BaseObserver extends ResourceObserver { - private static final String TAG = "BaseObserver"; - private boolean isShowLoadingView = true; - private boolean isShowErrorView =true; + private static final String TAG = "BaseObserver"; // 日志标签 + private boolean isShowLoadingView = true; // 是否显示加载视图 + private boolean isShowErrorView = true; // 是否显示错误视图 - private BaseView baseView; + private BaseView baseView; // 基础视图接口,用于更新UI - private BaseObserver(){} + // 私有构造函数,防止直接实例化 + private BaseObserver() {} - protected BaseObserver(BaseView baseView){ - this(baseView,false,false); + // 构造函数,初始化BaseView + protected BaseObserver(BaseView baseView) { + this(baseView, false, false); } - protected BaseObserver(BaseView baseView,boolean isShowLoadingView){ - this(baseView,isShowLoadingView,false); + // 构造函数,初始化BaseView和是否显示加载视图 + protected BaseObserver(BaseView baseView, boolean isShowLoadingView) { + this(baseView, isShowLoadingView, false); } - protected BaseObserver(BaseView baseView,boolean isShowLoadingView, boolean isShowErrorView){ + // 构造函数,初始化BaseView、是否显示加载视图和是否显示错误视图 + protected BaseObserver(BaseView baseView, boolean isShowLoadingView, boolean isShowErrorView) { this.baseView = baseView; this.isShowLoadingView = isShowLoadingView; this.isShowErrorView = isShowErrorView; } + /** + * 请求开始时调用,显示加载视图。 + */ @Override protected void onStart() { - if(isShowLoadingView) baseView.showLoading(); + if (isShowLoadingView) baseView.showLoading(); } - - + /** + * 请求成功时调用,隐藏加载视图,显示正常视图。 + * @param t 返回的数据 + */ @Override public void onNext(T t) { - new Handler().postDelayed(()->{ + new Handler().postDelayed(() -> { baseView.showNormalView(); - },500); - + }, 500); } + /** + * 请求失败时调用,处理各种异常情况并显示错误视图。 + * @param e 异常信息 + */ @Override public void onError(Throwable e) { - new Handler().postDelayed(()->{ - if(isShowErrorView) baseView.showErrorView(); - },500); + new Handler().postDelayed(() -> { + if (isShowErrorView) baseView.showErrorView(); + }, 500); e.printStackTrace(); + // 处理不同类型的异常 if (e instanceof UnknownHostException) { Log.e(TAG_ERROR, "networkError:" + e.getMessage()); networkError(); @@ -82,50 +95,51 @@ public class BaseObserver extends ResourceObserver { } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) { Log.e(TAG_ERROR, "解析错误:" + e.getMessage()); parseError(); - }else { + } else { Log.e(TAG_ERROR, "未知错误:" + e.getMessage()); unknown(); } } + /** + * 请求完成时调用,不做任何操作。 + */ @Override public void onComplete() { } /** - * 未知错误 + * 显示未知错误信息。 */ protected void unknown() { baseView.showToast(App.getContext().getString(R.string.error_unknown)); - } /** - * 解析错误 + * 显示解析错误信息。 */ protected void parseError() { baseView.showToast(App.getContext().getString(R.string.error_parse)); } /** - * http错误 + * 显示HTTP错误信息。 */ protected void httpError() { baseView.showToast(App.getContext().getString(R.string.error_http)); } /** - * 网络超时异常 + * 显示网络超时错误信息。 */ protected void timeoutError() { baseView.showToast(App.getContext().getString(R.string.error_timeout)); } /** - * 网络不可用异常 + * 显示网络不可用错误信息。 */ protected void networkError() { baseView.showToast(App.getContext().getString(R.string.error_network)); } - -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/presenter/BasePresenter.java b/app/src/main/java/com/example/musicplayer/base/presenter/BasePresenter.java index b0d01a6..1162d08 100644 --- a/app/src/main/java/com/example/musicplayer/base/presenter/BasePresenter.java +++ b/app/src/main/java/com/example/musicplayer/base/presenter/BasePresenter.java @@ -1,63 +1,76 @@ package com.example.musicplayer.base.presenter; -import android.provider.ContactsContract; - +// 导入所需的包和类 import com.example.musicplayer.base.view.BaseView; import com.example.musicplayer.model.DataModel; import com.example.musicplayer.model.db.DbHelperImpl; import com.example.musicplayer.model.https.NetworkHelperImpl; import com.example.musicplayer.model.https.RetrofitFactory; import com.example.musicplayer.model.prefs.PreferencesHelperImpl; - import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; /** - *

- *     author : 残渊
- *     time   : 2019/07/14
- *     desc   :
- * 
+ * MVP架构中的基Presenter类,用于处理业务逻辑。 + *

+ * author : 残渊 + * time : 2019/07/14 + * desc : 提供了Presenter的基本实现,包括视图的绑定与解绑,以及RxJava订阅的管理。 */ +public class BasePresenter implements IPresenter { -public class BasePresenter implements IPresenter{ - - protected T mView; - protected DataModel mModel; + protected T mView; // 用于与视图层交互的接口 + protected DataModel mModel; // 数据模型,用于处理数据逻辑 - //得到model - public BasePresenter(){ - if(mModel == null){ - mModel = new DataModel(new NetworkHelperImpl(RetrofitFactory.createRequest()),new DbHelperImpl(),new PreferencesHelperImpl()); + // 构造函数,初始化DataModel + public BasePresenter() { + if (mModel == null) { + mModel = new DataModel(new NetworkHelperImpl(RetrofitFactory.createRequest()), + new DbHelperImpl(), new PreferencesHelperImpl()); } } - private CompositeDisposable mCompositeDisposable; + + private CompositeDisposable mCompositeDisposable; // RxJava订阅容器 + + /** + * 将视图绑定到Presenter。 + * @param view 视图对象 + */ @Override public void attachView(T view) { mView = view; } + /** + * 检查是否有视图绑定到Presenter。 + * @return 是否有视图绑定 + */ @Override public boolean isAttachView() { return mView != null; } - //在presenter与View解除时将订阅事件切断 + /** + * 当Presenter与视图解除绑定时,清除所有的RxJava订阅以避免内存泄漏。 + */ @Override public void detachView() { mView = null; - //清除 - if(mCompositeDisposable != null){ + // 清除所有的RxJava订阅 + if (mCompositeDisposable != null) { mCompositeDisposable.clear(); } } - //网络请求时将订阅事件添加到容器中 + /** + * 将RxJava订阅添加到订阅容器中,以便统一管理。 + * @param disposable RxJava订阅对象 + */ @Override public void addRxSubscribe(Disposable disposable) { - if(mCompositeDisposable == null){ + if (mCompositeDisposable == null) { mCompositeDisposable = new CompositeDisposable(); } mCompositeDisposable.add(disposable); } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/presenter/IPresenter.java b/app/src/main/java/com/example/musicplayer/base/presenter/IPresenter.java index ff81c05..5b7bc73 100644 --- a/app/src/main/java/com/example/musicplayer/base/presenter/IPresenter.java +++ b/app/src/main/java/com/example/musicplayer/base/presenter/IPresenter.java @@ -1,20 +1,41 @@ package com.example.musicplayer.base.presenter; +// 导入所需的包和类 import com.example.musicplayer.base.view.BaseView; - import io.reactivex.disposables.Disposable; /** - *

- *     author : 残渊
- *     time   : 2019/07/14
- *     desc   : 抽象Presenter
- * 
+ * 定义了Presenter的基本行为,用于MVP架构。 + *

+ * author : 残渊 + * time : 2019/07/14 + * desc : 抽象Presenter接口,规定了Presenter需要实现的方法。 */ - public interface IPresenter { - void attachView(T view); //注入View - boolean isAttachView(); //判断是否注入View - void detachView(); //解除View - void addRxSubscribe(Disposable disposable);//添加订阅者 -} + /** + * 将视图(View)注入到Presenter中。 + * 这是MVP架构中View与Presenter交互的第一步,通常在View的onCreate或相似的生命周期方法中调用。 + * @param view 需要注入的视图对象,必须遵循BaseView接口。 + */ + void attachView(T view); + + /** + * 判断是否有视图已经注入到Presenter中。 + * 这个检查常用于判断是否可以进行UI操作,因为只有当View被注入后,Presenter才能与View进行交互。 + * @return true表示已经注入View,false表示没有注入View。 + */ + boolean isAttachView(); + + /** + * 从Presenter中解除视图(View)的绑定。 + * 这是MVP架构中View与Presenter交互的结束步骤,通常在View的onDestroy或相似的生命周期方法中调用。 + */ + void detachView(); + + /** + * 添加RxJava的订阅者(Disposable)到管理容器中。 + * 这个方法用于管理RxJava中的订阅生命周期,以避免内存泄漏。 + * @param disposable 需要添加的订阅者对象。 + */ + void addRxSubscribe(Disposable disposable); +} \ No newline at end of file diff --git a/app/src/main/java/com/example/musicplayer/base/view/BaseView.java b/app/src/main/java/com/example/musicplayer/base/view/BaseView.java index d3f21d8..2dd88d0 100644 --- a/app/src/main/java/com/example/musicplayer/base/view/BaseView.java +++ b/app/src/main/java/com/example/musicplayer/base/view/BaseView.java @@ -1,17 +1,41 @@ package com.example.musicplayer.base.view; /** - *

- *     author : 残渊
- *     time   : 2019/07/14
- *     desc   : 页面基类
- * 
+ * 定义了页面视图(View)的基本行为,用于MVP架构中的视图层。 + *

+ * author : 残渊 + * time : 2019/07/14 + * desc : 基类视图接口,规定了视图需要实现的方法,用于展示不同的视图状态和交互。 */ - public interface BaseView { - void showNormalView(); //正常布局 - void showErrorView(); //错误布局 - void showLoading();//加载布局 - void reload();//重新加载 - void showToast(String message);//显示Toast -} + /** + * 显示正常的视图布局。 + * 当数据加载完成并且没有错误时调用,用于展示内容给用户。 + */ + void showNormalView(); + + /** + * 显示错误的视图布局。 + * 当请求数据失败或者发生错误时调用,用于通知用户发生了错误。 + */ + void showErrorView(); + + /** + * 显示加载中的视图布局。 + * 当数据正在加载时调用,通常用于显示加载指示器。 + */ + void showLoading(); + + /** + * 重新加载数据。 + * 当用户触发重新加载操作时调用,如下拉刷新或者点击重试按钮。 + */ + void reload(); + + /** + * 显示Toast消息。 + * 用于给用户展示简短的消息提示。 + * @param message 要展示的消息内容。 + */ + void showToast(String message); +} \ No newline at end of file