Android简易音乐重构MVVM Java版-新增looklive+用户vip等级动画展示(十四)
关于
本篇主要实现新增look live列表展示、加载网易云会员动态等级效果。
效果图
新增looklive适配器(LookAdapter)
public class LookAdapter extends RecyclerView.Adapter<LookViewHolder> {
private final List<LookLiveEntity> dataList = new ArrayList<>();
private final Context mContext;
private OnItemClick onItemClick;
public void setOnItemClick(OnItemClick onItemClick) {
this.onItemClick = onItemClick;
}
public LookAdapter(Context context) {
this.mContext = context;
}
@NonNull
@Override
public LookViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemRecommendDiscoverBinding binding = ItemRecommendDiscoverBinding
.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new LookViewHolder(binding);
}
@SuppressLint("NotifyDataSetChanged")
public void setDataList(List<LookLiveEntity> data) {
dataList.clear();
dataList.addAll(data);
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(@NonNull LookViewHolder holder, int position) {
LookLiveEntity bean = dataList.get(position);
holder.tvTitle.setText(bean.getTitle());
//holder.tvCount.setText(bean.getResources().get(0).getResourceExtInfo().getPlayCount());
RequestOptions options = new RequestOptions()
.placeholder(R.drawable.ic_banner_loading)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.transform(new CenterCrop(),new RoundedCorners(10))
.error(R.mipmap.ic_launcher);
Glide.with(mContext)
.load(bean.getVerticalCover())
.transition(new DrawableTransitionOptions().crossFade())
.apply(options)
.into(holder.imRecommend);
}
@Override
public int getItemCount() {
return dataList.size();
}
}
class LookViewHolder extends RecyclerView.ViewHolder {
TextView tvTitle, tvCount;
ImageView imRecommend;
public LookViewHolder(ItemRecommendDiscoverBinding binding) {
super(binding.getRoot());
tvTitle = binding.recommendTitle;
imRecommend = binding.imgRecommend;
tvCount = binding.playCount;
}
}
新增LookLiveEntity实体类
@NoArgsConstructor
@Data
public class LookLiveEntity {
private int liveId;
private String title;
private double anchorId;
private long coverId;
private String cover;
private LiveUrlEntity liveUrl;
private Object playBackUrl;
private int orientationScope;
private int onlineNumber;
private int liveStatus;
private long startTime;
private int endTime;
private int roomId;
private Object channelId;
private int liveType;
private int appKeyType;
private int type;
private int startStreamTag;
private int agoraRoomNo;
private String bgCoverUrl;
private Object backgroundAnimateUrl;
private int rtcSupplierType;
private int popularity;
private long verticalCoverId;
private String verticalCover;
private UserInfoEntity userInfo;
private RecLiveDTOEntity recLiveDTO;
private String coverTag;
private String privateTag;
private Object borderTag;
private String startStreamTagName;
private Object tags;
private Object dynamicCover;
private List<String> audioCoverIds;
private SupplementParamToClientEntity supplementParamToClient;
private CloudMusicMyFollowRecInfoEntity cloudMusicMyFollowRecInfo;
private Object adSpreadDto;
@NoArgsConstructor
@Data
public static class LiveUrlEntity {
private String httpPullUrl;
private String hlsPullUrl;
private String rtmpPullUrl;
}
@NoArgsConstructor
@Data
public static class UserInfoEntity {
private double userId;
private String nickname;
private String avatarUrl;
private int authStatus;
private int userType;
private Object authName;
private int liveRoomNo;
private int vipType;
private int gender;
private Object artistName;
}
@NoArgsConstructor
@Data
public static class RecLiveDTOEntity {
private String skipUrl;
private String typeDesc;
private int cardType;
private String alg;
private String anchorId;
private int liveRoomNo;
private int songId;
private int accompanimentId;
private SupplemetParamEntity supplemetParam;
private String ops;
private long recCoverId;
private String recCover;
private String coverTag;
private int segId;
@NoArgsConstructor
@Data
public static class SupplemetParamEntity {
private String hp_moduletitle;
private String coverID;
private String liveOnlineNumber;
}
}
@NoArgsConstructor
@Data
public static class SupplementParamToClientEntity {
private String ops;
}
@NoArgsConstructor
@Data
public static class CloudMusicMyFollowRecInfoEntity {
}
}
修改DiscoverFragment布局
修改DiscoverFragment
//第一步
private LookAdapter lookAdapter;
//第二步
lookAdapter = new LookAdapter(getContext());
LinearLayoutManager managerLook = new LinearLayoutManager(getContext());
managerLook.setOrientation(LinearLayoutManager.HORIZONTAL);
binding.lookRecycle.setLayoutManager(managerLook);
binding.lookRecycle.setHasFixedSize(true);
binding.lookRecycle.setAdapter(lookAdapter);
//第三步添加数据
private void initObserver() {
viewModel.requireDiscover(false).observe(getViewLifecycleOwner(), homeDiscoverEntityApiResponse -> {
if (homeDiscoverEntityApiResponse.getStatus() == Status.SUCCESS){
for (HomeDiscoverEntity.DataEntity.BlocksEntity block : homeDiscoverEntityApiResponse.getData().getData().getBlocks()){
switch (block.getBlockCode()){
case "HOMEPAGE_BANNER": //banner
String bannerJson = new Gson().toJson(block.getExtInfo());
viewModel.bannerList = new Gson().fromJson(bannerJson,BannerExtInfoEntity.class);
break;
case "HOMEPAGE_BLOCK_PLAYLIST_RCMD": //推荐歌单
viewModel.recommendList = block.getCreatives();
break;
case "HOMEPAGE_BLOCK_LISTEN_LIVE"://直播
binding.tvLook.setText(block.getUiElement().getSubTitle().getTitle());
binding.tvLookMore.setText(block.getUiElement().getButton().getText());
String lookJson = new Gson().toJson(block.getExtInfo());
viewModel.lookLiveList = new Gson().fromJson(lookJson,new TypeToken<List<LookLiveEntity>>(){}.getType());
break;
case "HOMEPAGE_BLOCK_MGC_PLAYLIST"://雷达歌单
viewModel.selfMgcList = block.getCreatives();
binding.tvMgc.setText(block.getUiElement().getSubTitle().getTitle());
binding.tvMgcMore.setText(block.getUiElement().getButton().getText());
break;
}
binding.tvBottom.setText(homeDiscoverEntityApiResponse.getData().getData().getPageConfig().getNodataToast());
}
initData(viewModel.bannerList.getBanners());
adapter.setDataList(viewModel.recommendList);
mgcAdapter.setDataList(viewModel.selfMgcList);
lookAdapter.setDataList(viewModel.lookLiveList);
}else {
Log.e("报错",homeDiscoverEntityApiResponse.getMessage());
}
});
}
修改DiscoverFragmentViewModel
public class DiscoverFragmentViewModel extends ViewModel {
public String date = Calendar.getInstance().get(Calendar.DAY_OF_MONTH) + "";
public BannerExtInfoEntity bannerList;
public List<HomeDiscoverEntity.DataEntity.BlocksEntity.CreativesEntity> recommendList;
public List<HomeDiscoverEntity.DataEntity.BlocksEntity.CreativesEntity> selfMgcList;
public List<LookLiveEntity> lookLiveList;
public LiveData<ApiResponse<HomeDiscoverEntity>> requireDiscover(Boolean refresh){
return RetrofitUtils.getmApiUrl().requireHomeDiscover(refresh);
}
}
新增本地html(vip.html
)
新增文件assets/web/vip.html:
<html style="height: 100%;">
<head>
<meta name="viewport" content="width=device-width, user-scalable=no"><title>75352cbecbf8939e377cf67342546dda.png (135×51)</title>
</head>
<body style="margin: 0px; background: #00000000; height: 100%">
<img style="display: block;-webkit-user-select: none;max-width: 100%;margin: auto;" src="https://p5.music.126.net/obj/wo3DlcOGw6DClTvDisK1/4417174746/b7e0/c76d/01b9/75352cbecbf8939e377cf67342546dda.png">
</body>
</html>
对应网页vip效果如下:
引用三方lib
//加载网页
implementation 'com.github.Tobeyr1:webLoading:1.0.1'
//Jsoup
implementation 'org.jsoup:jsoup:1.13.1'
修改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" />
</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_title_bg"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_45"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/colorPrimary"
/>
<View
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_8"
android:layout_marginEnd="@dimen/dp_8"
/>
<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_toBottomOf="@id/view_title_bg"
/>
<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"
/>
<TextView
android:id="@+id/tv_follow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.ui.follows}"
android:textColor="@color/grays_06"
android:layout_marginTop="@dimen/dp_50"
app:layout_constraintTop_toBottomOf="@id/img_head"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
新增UserInfoUi
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 UserInfoUi(ObservableField<String> imageUrl, ObservableField<String> nickname, ObservableInt userId,ObservableField<String> signature, ObservableField<String> follows, ObservableField<String> followeds) {
this.nickname = nickname;
this.imageUrl = imageUrl;
this.userId = userId;
this.signature = signature;
this.follows = follows;
this.followeds = followeds;
}
}
修改MainViewModel
public class MainViewModel extends ViewModel {
private SavedStateHandle state;
public UserInfoUi ui;
private String userInfo;
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<>("")) : 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()+"粉丝");
}
}
}
新增MineFragmentViewModel(我的页面的viewmodel)
public class MineFragmentViewModel extends ViewModel {
public LiveData<ApiResponse<VipInfoEntity>> getVipInfo(){
return RetrofitUtils.getmApiUrl().getVipInfo();
}
}
新增vip信息实体类
@NoArgsConstructor
@Data
public class VipInfoEntity {
private String message;
private DataEntity data;
private int code;
@NoArgsConstructor
@Data
public static class DataEntity {
private String redVipLevelIcon;
private int redVipLevel;
private int redVipAnnualCount;
private MusicPackageEntity musicPackage;
private AssociatorEntity associator;
private String redVipDynamicIconUrl;
private String redVipDynamicIconUrl2;
@NoArgsConstructor
@Data
public static class MusicPackageEntity {
private int vipCode;
private long expireTime;
private boolean isSignDeduct;
private boolean isSign;
private boolean isSignIapDeduct;
private boolean isSignIap;
}
@NoArgsConstructor
@Data
public static class AssociatorEntity {
private int vipCode;
private long expireTime;
private boolean isSignDeduct;
private boolean isSign;
private boolean isSignIapDeduct;
private boolean isSignIap;
}
}
}
新增请求vip接口
@GET("vip/info")//vip信息
LiveData<ApiResponse<VipInfoEntity>> getVipInfo();
新增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);
homeViewModel = new ViewModelProvider(this).get(MainViewModel.class);
viewModel = new ViewModelProvider(this).get(MineFragmentViewModel.class);
binding.setLifecycleOwner(this);
binding.setVm(homeViewModel);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
homeViewModel.initUi();
initWebView();
initObserver();
}
@SuppressLint("SetJavaScriptEnabled")
private void initWebView() {
binding.webVip.getSettings().setJavaScriptEnabled(true);
}
private void initObserver() {
viewModel.getVipInfo().observe(getViewLifecycleOwner(), vipInfoEntityApiResponse -> {
if (vipInfoEntityApiResponse.getStatus() == Status.SUCCESS){
binding.webVip.loadDataWithBaseURL(null,changeImageUrl(vipInfoEntityApiResponse.getData().getData().getRedVipDynamicIconUrl2()),"text/html", "utf-8", null);
}
});
}
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();
}
}
到此文章结束,有问题欢迎批评指正,觉得不错的也请点个赞