Android简易音乐重构MVVM Java版-新增我喜欢的歌单展示+底部bar点击跳转播放功能(十九)
关于
效果
修改MainActivity
新增底部bar初始化
private void initBottomBar() {
viewModel.getRecentSong().observe(this,recentSongInfoEntityApiResponse -> {
if (recentSongInfoEntityApiResponse.getStatus() == Status.SUCCESS && recentSongInfoEntityApiResponse.getData().getData().getList().size() >0){
RecentSongInfoEntity.RecentDataEntity.ListEntity.DataEntity data =recentSongInfoEntityApiResponse.getData().getData().getList()
.get(0).getData();
viewModel.currentSongName.set(data.getName());
viewModel.currentSongUrl.set(data.getAl().getPicUrl());
MusicInfo musicInfo = new MusicInfo();
musicInfo.setArtist(data.getAr().get(0).getName());
musicInfo.setSongId(data.getId()+"");
musicInfo.setSongName(data.getName());
musicInfo.setSongCover(data.getAl().getPicUrl());
musicInfo.setSongUrl(SONG_URL+data.getId());
viewModel.currentMusicInfo = musicInfo;
}
});
MusicPlay.onPlayStateListener(this, new OnMusicPlayStateListener() {
@Override
public void onPlayState(@NonNull PlayManger playManger) {
viewModel.currentSongUrl.set(playManger.getSongInfo().getSongCover());
viewModel.currentSongName.set(playManger.getSongInfo().getSongName());
switch (playManger.getStage()){
case PlayManger.PAUSE:
case PlayManger.IDLE:
binding.songBar.ivBottomPlay.setImageResource(R.drawable.shape_play);
break;
case PlayManger.PLAYING:
binding.songBar.ivBottomPlay.setImageResource(R.drawable.shape_pause_black);
viewModel.currentMusicInfo = playManger.getSongInfo();
break;
case PlayManger.BUFFERING:
ViewExtensionKt.printLog("缓冲");
break;
case PlayManger.SWITCH:
viewModel.currentMusicInfo = playManger.getSongInfo();
break;
}
}
});
binding.songBar.rootBottomBar.setOnClickListener(view -> {
if (ClickUtil.enableClick()){
MusicPlay.playMusicByInfo(viewModel.currentMusicInfo);
startActivity(new Intent(this, CurrentSongPlayActivity.class)
.putExtra(MUSIC_INFO, viewModel.currentMusicInfo));
}
});
binding.songBar.ivBottomPlay.setOnClickListener(view -> {
if (MusicPlay.isPlaying()){
MusicPlay.pauseMusic();
}else if (MusicPlay.isPaused()){
MusicPlay.restoreMusic();
}else {
MusicPlay.playMusicByInfo(viewModel.currentMusicInfo);
}
});
}
修改fragment_mine.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="com.tobery.personalmusic.ui.home.MainViewModel" />
<variable
name="my"
type="com.tobery.personalmusic.ui.home.mine.MineFragmentViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grays_10">
<View
android:id="@+id/view_nike"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_0"
android:layout_marginTop="@dimen/dp_40"
android:background="@drawable/bg_view_white_full"
android:paddingBottom="@dimen/dp_24"
app:layout_constraintTop_toTopOf="@id/img_head"
app:layout_constraintBottom_toBottomOf="@id/tv_follow"
android:padding="@dimen/dp_24"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
/>
<ImageView
android:id="@+id/img_head"
android:layout_width="@dimen/dp_80"
android:layout_height="@dimen/dp_80"
imSrc="@{vm.ui.imageUrl}"
error="@{@drawable/ic_banner_loading}"
android:layout_margin="@dimen/dp_16"
android:elevation="@dimen/dp_8"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<WebView
android:id="@+id/web_vip"
android:layout_width="@dimen/dp_40"
android:layout_height="@dimen/dp_20"
app:layout_constraintBottom_toBottomOf="@id/tv_nickname"
app:layout_constraintStart_toEndOf="@id/tv_nickname"
android:layout_marginStart="@dimen/dp_5"
/>
<TextView
android:id="@+id/tv_nickname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.ui.nickname}"
android:textColor="@color/black"
android:textSize="@dimen/sp_18"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/img_head"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="@dimen/dp_8"
/>
<TextView
android:id="@+id/tv_follow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.ui.follows}"
android:textSize="@dimen/sp_14"
android:textColor="@color/grays_66"
android:layout_marginEnd="@dimen/dp_16"
android:paddingBottom="@dimen/dp_8"
app:layout_constraintTop_toBottomOf="@id/tv_nickname"
app:layout_constraintEnd_toStartOf="@id/tv_fans"
/>
<TextView
android:id="@+id/tv_fans"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.ui.followeds}"
android:textSize="@dimen/sp_14"
android:textColor="@color/grays_66"
app:layout_constraintTop_toBottomOf="@id/tv_nickname"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<TextView
android:id="@+id/tv_level"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{my.level}"
android:textSize="@dimen/sp_14"
android:textColor="@color/grays_66"
android:layout_marginStart="@dimen/dp_16"
app:layout_constraintTop_toBottomOf="@id/tv_nickname"
app:layout_constraintStart_toEndOf="@id/tv_fans"
/>
<View
android:id="@+id/view_like_item"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_80"
app:layout_constraintTop_toBottomOf="@id/view_nike"
android:background="@drawable/bg_view_white_full"
android:layout_margin="@dimen/dp_16"
/>
<ImageView
android:id="@+id/img_cover"
android:layout_width="@dimen/dp_60"
android:layout_height="@dimen/dp_60"
app:layout_constraintTop_toTopOf="@id/view_like_item"
app:layout_constraintBottom_toBottomOf="@id/view_like_item"
app:layout_constraintStart_toStartOf="@id/view_like_item"
android:layout_marginStart="@dimen/dp_16"
rectangleSrc="@{my.mineLikeCover}"
/>
<TextView
android:id="@+id/tv_introduction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:text="music"
android:textSize="@dimen/sp_16"
android:layout_marginStart="@dimen/dp_8"
app:layout_constraintTop_toTopOf="@id/view_like_item"
app:layout_constraintBottom_toTopOf="@id/tv_count"
app:layout_constraintStart_toEndOf="@id/img_cover"
/>
<TextView
android:id="@+id/tv_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/grays_66"
android:text="@{my.trackCount}"
android:textSize="@dimen/sp_12"
android:layout_marginStart="@dimen/dp_8"
app:layout_constraintTop_toBottomOf="@id/tv_introduction"
app:layout_constraintBottom_toBottomOf="@id/view_like_item"
app:layout_constraintStart_toEndOf="@id/img_cover"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
修改MainViewModel
public class MainViewModel extends ViewModel {
private SavedStateHandle state;
public UserInfoUi ui;
private String userInfo;
public ObservableField<String> currentSongUrl = new ObservableField<>("");
public ObservableField<String> currentSongName = new ObservableField<>("");
public MusicInfo currentMusicInfo;
public MainViewModel(SavedStateHandle savedStateHandle) {
this.state = savedStateHandle;
ui = state.get(KEY_MAIN_UI) == null ? new UserInfoUi(new ObservableField<>(""), new ObservableField<>(""), new ObservableInt(0), new ObservableField<>(""), new ObservableField<>(""), new ObservableField<>(""), new ObservableField<>("")) : state.get(KEY_MAIN_UI);
}
public void initUi() {
userInfo = SharePreferencesUtil.getInstance(ContextProvider.get().getContext())
.getUserInfo();
LoginEntity data = new Gson().fromJson(userInfo, LoginEntity.class);
if (null != data.getProfile()) {
ui.nickname.set(data.getProfile().getNickname());
ui.imageUrl.set(data.getProfile().getAvatarUrl());
ui.userId.set(data.getProfile().getUserId());
ui.signature.set(data.getProfile().getSignature());
ui.follows.set(data.getProfile().getFollows()+"关注");
ui.followeds.set(data.getProfile().getFolloweds()+"粉丝");
}
}
public LiveData<ApiResponse<UserDetailEntity>> getUserDetails() {
return RetrofitUtils.getmApiUrl().getUserDetails(ui.userId.get());
}
public LiveData<ApiResponse<RecentSongInfoEntity>> getRecentSong(){
return RetrofitUtils.getmApiUrl().getRecentSong(1);
}
}
添加用户profile
public class UserInfoUi implements Serializable {
public ObservableField<String> imageUrl;
public ObservableField<String> nickname;//昵称
public ObservableInt userId;
public ObservableField<String> signature;//个签
public ObservableField<String> follows;//关注
public ObservableField<String> followeds;//粉丝
public ObservableField<String> level;//用户等级
public UserInfoUi(ObservableField<String> imageUrl, ObservableField<String> nickname, ObservableInt userId,ObservableField<String> signature, ObservableField<String> follows, ObservableField<String> followeds,ObservableField<String> level) {
this.nickname = nickname;
this.imageUrl = imageUrl;
this.userId = userId;
this.signature = signature;
this.follows = follows;
this.followeds = followeds;
this.level = level;
}
}
添加用户信息接口
@GET("user/detail")//用户信息
LiveData<ApiResponse<UserDetailEntity>> getUserDetails(@Query("uid") long userId);
@GET("user/playlist") //用户歌单
LiveData<ApiResponse<UserPlayEntity>> getUserPlayList(@Query("uid") long userId);
添加用户实体类UserDetailEntity
@NoArgsConstructor
@Data
public class UserDetailEntity {
private int level;
private int listenSongs;
private UserPointEntity userPoint;
private boolean mobileSign;
private boolean pcSign;
private ProfileEntity profile;
private boolean peopleCanSeeMyPlayRecord;
private List<BindingsEntity> bindings;
private boolean adValid;
private int code;
private long createTime;
private int createDays;
private ProfileVillageInfoEntity profileVillageInfo;
@NoArgsConstructor
@Data
public static class UserPointEntity {
private int userId;
private int balance;
private long updateTime;
private int version;
private int status;
private int blockBalance;
}
@NoArgsConstructor
@Data
public static class ProfileEntity {
private PrivacyItemUnlimitEntity privacyItemUnlimit;
private Object avatarDetail;
private String backgroundImgIdStr;
private String avatarImgIdStr;
private String description;
private int userId;
private int vipType;
private int userType;
private long createTime;
private String nickname;
private String avatarUrl;
private int gender;
private boolean mutual;
private boolean followed;
private Object remarkName;
private int authStatus;
private String detailDescription;
private ExpertsEntity experts;
private Object expertTags;
private int djStatus;
private int accountStatus;
private int province;
private int city;
private boolean defaultAvatar;
private long backgroundImgId;
private String backgroundUrl;
private long birthday;
private long avatarImgId;
private String signature;
private int authority;
private int followeds;
private int follows;
private boolean blacklist;
private int eventCount;
private int allSubscribedCount;
private int playlistBeSubscribedCount;
private String avatarImgId_str;
private Object followTime;
private boolean followMe;
private List<?> artistIdentity;
private int cCount;
private boolean inBlacklist;
private int sDJPCount;
private int playlistCount;
private int sCount;
private int newFollows;
@NoArgsConstructor
@Data
public static class PrivacyItemUnlimitEntity {
private boolean area;
private boolean college;
private boolean age;
private boolean villageAge;
}
@NoArgsConstructor
@Data
public static class ExpertsEntity {
}
}
@NoArgsConstructor
@Data
public static class ProfileVillageInfoEntity {
private String title;
private Object imageUrl;
private String targetUrl;
}
@NoArgsConstructor
@Data
public static class BindingsEntity {
private boolean expired;
private String url;
private int userId;
private int expiresIn;
private int refreshTime;
private long bindingTime;
private Object tokenJsonStr;
private long id;
private int type;
}
}
修改MineFragmentViewModel
public class MineFragmentViewModel extends ViewModel {
public ObservableField<String> mineLikeCover = new ObservableField<>("");
public ObservableField<String> trackCount = new ObservableField<>("");
public ObservableField<String> level = new ObservableField<>("");
public Long userLikeCreator = 0L;
public LiveData<ApiResponse<VipInfoEntity>> getVipInfo(){
return RetrofitUtils.getmApiUrl().getVipInfo();
}
//获取用户歌单
public LiveData<ApiResponse<UserPlayEntity>> getUserPlayList(Long userId){
return RetrofitUtils.getmApiUrl().getUserPlayList(userId);
}
}
添加用户歌单实体类UserPlayEntity
@NoArgsConstructor
@Data
public class UserPlayEntity {
private String version;
private boolean more;
private List<PlaylistEntity> playlist;
private int code;
@NoArgsConstructor
@Data
public static class PlaylistEntity {
private List<?> subscribers;
private boolean subscribed;
private CreatorEntity creator;
private Object artists;
private Object tracks;
private Object updateFrequency;
private long backgroundCoverId;
private Object backgroundCoverUrl;
private long titleImage;
private Object titleImageUrl;
private Object englishTitle;
private boolean opRecommend;
private Object recommendInfo;
private int subscribedCount;
private int cloudTrackCount;
private int userId;
private int totalDuration;
private long coverImgId;
private int privacy;
private long trackUpdateTime;
private int trackCount;
private long updateTime;
private String commentThreadId;
private String coverImgUrl;
private int specialType;
private boolean anonimous;
private long createTime;
private boolean highQuality;
private boolean newImported;
private long trackNumberUpdateTime;
private long playCount;
private int adType;
private Object description;
private List<?> tags;
private boolean ordered;
private int status;
private String name;
private long id;
private String coverImgId_str;
private Object sharedUsers;
private Object shareStatus;
@NoArgsConstructor
@Data
public static class CreatorEntity {
private boolean defaultAvatar;
private int province;
private int authStatus;
private boolean followed;
private String avatarUrl;
private int accountStatus;
private int gender;
private int city;
private int birthday;
private int userId;
private int userType;
private String nickname;
private String signature;
private String description;
private String detailDescription;
private long avatarImgId;
private long backgroundImgId;
private String backgroundUrl;
private int authority;
private boolean mutual;
private Object expertTags;
private Object experts;
private int djStatus;
private int vipType;
private Object remarkName;
private int authenticationTypes;
private Object avatarDetail;
private boolean anchor;
private String avatarImgIdStr;
private String backgroundImgIdStr;
private String avatarImgId_str;
}
}
}
修改MineFragment
public class MineFragment extends Fragment {
private FragmentMineBinding binding;
private MainViewModel homeViewModel;
private MineFragmentViewModel viewModel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = FragmentMineBinding.inflate(inflater,container,false);
homeViewModel = new ViewModelProvider(this).get(MainViewModel.class);
viewModel = new ViewModelProvider(this).get(MineFragmentViewModel.class);
binding.setLifecycleOwner(this);
binding.setVm(homeViewModel);
binding.setMy(viewModel);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
homeViewModel.initUi();
initWebView();
initView();
initObserver();
}
private void initView() {
binding.viewLikeItem.setOnClickListener(v -> {
if (ClickUtil.enableClick()){
startActivity(new Intent(getActivity(), MinePlayListActivity.class)
.putExtra(PLAYLIST_ID,viewModel.userLikeCreator)
.putExtra(PLAY_NAME,"歌单"));
}
});
}
@SuppressLint("SetJavaScriptEnabled")
private void initWebView() {
binding.webVip.getSettings().setJavaScriptEnabled(true);
}
private void initObserver() {
viewModel.getVipInfo().observe(getViewLifecycleOwner(), vipInfoEntityApiResponse -> {
if (vipInfoEntityApiResponse.getStatus() == Status.SUCCESS && vipInfoEntityApiResponse.getData().getData().getRedVipDynamicIconUrl2()!= null){
binding.webVip.loadDataWithBaseURL(null,changeImageUrl(vipInfoEntityApiResponse.getData().getData().getRedVipDynamicIconUrl2()),"text/html", "utf-8", null);
}
});
homeViewModel.getUserDetails().observe(getViewLifecycleOwner(), userDetailEntityApiResponse -> {
if (userDetailEntityApiResponse.getStatus() == Status.SUCCESS){
viewModel.level.set("Lv."+userDetailEntityApiResponse.getData().getLevel());
}
});
viewModel.getUserPlayList(Long.valueOf(homeViewModel.ui.userId.get())).observe(getViewLifecycleOwner(), userPlayEntityApiResponse -> {
ViewExtensionKt.printLog(userPlayEntityApiResponse.getMessage());
if (userPlayEntityApiResponse.getStatus() == Status.SUCCESS){
UserPlayEntity.PlaylistEntity userLike = userPlayEntityApiResponse.getData().getPlaylist().get(0);
viewModel.mineLikeCover.set(userLike.getCoverImgUrl());
viewModel.trackCount.set(userLike.getTrackCount()+"");
viewModel.userLikeCreator = userLike.getId();
}
});
}
private String changeImageUrl(String url) {
Document doc = null;
try {
InputStream file = getResources().getAssets().open("web/vip.html");
doc = Jsoup.parse(file, "UTF-8", url);
Elements pngs = doc.select("img[src$=.png]");
pngs.attr("src",url);
file.close();
} catch (IOException e) {
e.printStackTrace();
}
assert doc != null;
return doc.toString();
}
}