Kotlin项目实战之手机影音---加载mv界面区域数据、mv界面viewpager适配、tablayout适配、mv每一个界面列表显示

本文主要是介绍Kotlin项目实战之手机影音---加载mv界面区域数据、mv界面viewpager适配、tablayout适配、mv每一个界面列表显示,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

修复API:

距离上一次Kotlin项目实战之手机影音---悦单条目实现及BaseListFragment抽取的这个Kotlin项目的学习间隔一年多了,完成已经消失在自己的记忆当中了,当然不能半途而废,继续接着前行,然后运行把工程重新运行一下,发现项目起不来了,尴尬。。

原因是由于音乐的API的域名访问不了了。。其实也正常,因为在之前Kotlin项目实战之手机影音---主界面tab切换、home界面适配、获得首页网络数据就说音乐数据是从网上找的,比如进首页的api为:

然后到浏览器访问一下:

寻找新的网上音乐API:

只能另找一些免费的数据源了【如果之后又被封了,我打算自己搭建个后台mock数据得了,要不用node服务器,要不用springboot,总之办法大于困难~~】,在网上搜到个这个免费稳定 => 网易云音乐API_余光的博客-CSDN博客_免费音乐api,打开:

然后到github上GitHub - Binaryify/NeteaseCloudMusicApi: 网易云音乐 Node.js API service把源码下来,按官方步骤安装运行一下:

然后输入“http://localhost:3000/”,此时会打开如下页面:

然后点击“查看文档”,此时可以看到列了一大堆接口,那哪个能为咱们项目所用呢?找来找去,没有发现特别适合的,不过这个勉强能用:

怎么用呢?这样访问:http://localhost:3000/mv/all?area=%E6%B8%AF%E5%8F%B0&limit=2&offset=0

那咱们这个项目是支持音频和视频的播放,这里获取的是mv列表,而具体mv的地址需要通过另一个接口获取,如下:

其中请求的id参数来自于它:

那。。有时候会有音频mp3,那咋搞呢?这里打算用一个写死的mp3地址作为列表中的一个实体元素就行了,完全不影响咱们的学习。

修改API:

接下来,则来修改一下项目的API,目前已实现的页面中涉及到两个URL,如下:

package com.kotlin.musicplayer.utils;import android.util.Log;public class URLProviderUtils {/*** 获取首页的url** @param offset 数据偏移量* @param size   返回数据的条目个数* @return url*/public static String getHomeUrl(int offset, int size) {String url = "http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.0&method=baidu.ting.artist.getSongList&format=json&order=2&tinguid=7988&artistid=7988"+ "&offset=" + offset+ "&limits=" + size;Log.i("Main_url", url);return url;}/*** 获取悦单列表的url*/public static String getYueDanUrl(int offset, int size) {String url = "http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.0&method=baidu.ting.artist.getSongList&format=json&order=2&tinguid=7988&artistid=7988"+ "&offset=" + offset+ "&limits=" + size;Log.i("Main_url", url);return url;}
}

这里修改成网易云音乐的API:

然后修改对应的实体,如下:

package com.kotlin.musicplayer.model/*** home界面每个条目的bean*/
data class HomeBean(val data: List<HomeItemBean>
)data class HomeItemBean(val artistId: Int,val artistName: String,val briefDesc: Any,val cover: String,val desc: Any,val duration: Int,val id: Int,val mark: Int,val name: String,val playCount: Int,val subed: Boolean
)
package com.kotlin.musicplayer.model/*** 悦单列表bean*/
data class YueDanBean(val data: List<YueDanItemBean>
)data class YueDanItemBean(val artistId: Int,val artistName: String,val briefDesc: Any,val cover: String,val desc: Any,val duration: Int,val id: Int,val mark: Int,val name: String,val playCount: Int,val subed: Boolean
)

其中这两个界面都是需要有mp3音频地址的对吧,所以这里各增加一个字段,然后里面写死一个在线的mp3地址:

package com.kotlin.musicplayer.model/*** home界面每个条目的bean*/
data class HomeBean(val data: List<HomeItemBean>
)data class HomeItemBean(val artistId: Int,val artistName: String,val briefDesc: Any,val cover: String,val desc: Any,val duration: Int,val id: Int,val mark: Int,val name: String,val playCount: Int,val subed: Boolean,val audioUrl: String = "http://m8.music.126.net/20210817054609/f3b6ded4f4a9321b6a26d26cfacc135f/ymusic/obj/w5zDlMODwrDDiGjCn8Ky/3047366729/c22a/6e45/ff05/31dbb8ef2bffa556d44aa24306e0ce1f.mp3"
)
package com.kotlin.musicplayer.model/*** 悦单列表bean*/
data class YueDanBean(val data: List<YueDanItemBean>
)data class YueDanItemBean(val artistId: Int,val artistName: String,val briefDesc: Any,val cover: String,val desc: Any,val duration: Int,val id: Int,val mark: Int,val name: String,val playCount: Int,val subed: Boolean,val audioUrl: String = "http://m8.music.126.net/20210817054609/f3b6ded4f4a9321b6a26d26cfacc135f/ymusic/obj/w5zDlMODwrDDiGjCn8Ky/3047366729/c22a/6e45/ff05/31dbb8ef2bffa556d44aa24306e0ce1f.mp3"
)

其中audioUrl的这个歌曲也是通过这个网易API来获取的,真的挺方便的,我想要林俊杰-曹操这首歌,然后先找到这位歌手的id:

然后再通过这个API来获取这位歌手的所有歌曲,其中找到想要歌曲的id信息:

然后再根据这个id来用另一个API获取具体的mp3地址:

另外注意,这里的字段重命名了:

修改Adapter:

由于model的字段变了,列表的显示也得进行调整,如下:

运行:

ok,环境恢复了,接下来可以继续前行了。

加载mv界面区域数据:

效果:

搭建布局:

对于这种滑动效果可以使用Indicator+ViewPager来实现,但是在Android5.0之后提供了一个TabLayout组件,咱们这里来使用它,如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.google.android.material.tabs.TabLayoutandroid:id="@+id/tabLayout"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/colorPrimary"app:tabIndicatorColor="#fff"app:tabIndicatorHeight="2dp"app:tabMode="scrollable"app:tabSelectedTextColor="#fff"app:tabTextColor="#40000000" /><androidx.viewpager.widget.ViewPagerandroid:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

加载tab音乐分类数据:

很显然对于上面的tab是从网络上来获取的,这边正好也有现成的接口:

创建MVView接口:

该接口是用来实现P层数据回调给UI层的,这里先新建一下,里面的接口方法之后用到时再来定义:

package com.kotlin.musicplayer.viewimport com.kotlin.musicplayer.model.MVTaginterface MVView {fun onError(msg: String?)fun onSuccess(result: List<MVTag>)
}

其中这里不像之前的页面一样去继承BaseView了:

因为没有下拉刷新列表动作,然后涉及到一个model:

package com.kotlin.musicplayer.modeldata class MvAreaBean(val tags: List<MVTag>
)data class MVTag(val category: Int,val hot: Boolean,val id: Int,val name: String,val type: Int
)

创建P层:

package com.kotlin.musicplayer.presenter.`interface`interface MvPresenter {fun loadDatas()
}
package com.kotlin.musicplayer.presenter.implimport com.kotlin.musicplayer.model.MvAreaBean
import com.kotlin.musicplayer.net.ResponseHandler
import com.kotlin.musicplayer.presenter.`interface`.MvPresenter
import com.kotlin.musicplayer.view.MVViewclass MvPresenterImpl(var mvView: MVView) : MvPresenter, ResponseHandler<MvAreaBean> {override fun loadDatas() {}override fun onError(type: Int, msg: String?) {TODO("Not yet implemented")}override fun onSuccessed(type: Int, result: MvAreaBean) {TODO("Not yet implemented")}
}

界面中调用P:

实现数据加载的逻辑:

package com.kotlin.musicplayer.netimport com.kotlin.musicplayer.model.MvAreaBean
import com.kotlin.musicplayer.utils.URLProviderUtils/*** mv区域数据请求*/
class MvAreaRequest(handler: ResponseHandler<MvAreaBean>) :MRequest<MvAreaBean>(0, URLProviderUtils.getMVareaUrl(), handler) {
}

然后在界面简单处理一下结果回调,目前先不处理列表显示,只先打个toast出来,先确保接口是畅通的:

运行:

mv界面viewpager适配:

准备PagerAdapter:

package com.kotlin.musicplayer.adapterimport android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import com.kotlin.musicplayer.model.MVTag
import com.kotlin.musicplayer.ui.fragment.MvPagerFragment/*** MV PagerAdapter*/
class MvPagerAdapter(val list: List<MVTag>?, fm: FragmentManager?) :FragmentPagerAdapter(fm) {override fun getItem(position: Int): Fragment {//第一种数据传递方式val fragment = MvPagerFragment()val bundle = Bundle()bundle.putString("args", list?.get(position)?.name)fragment.arguments = bundlereturn fragment}override fun getCount(): Int {return list?.size ?: 0//如果不为null返回list.size 为空返回0}override fun getPageTitle(position: Int): CharSequence? {return list?.get(position)?.name}
}

其中要注意的是getPageTitile,指的是Indicator的显示文案。接下来准备Fragment:

绑定Adapter:

接下来则回到界面上当数据请求成功之后绑定一下Adapter,如下:

运行:

然后运行看一下效果:

mv每一个界面列表显示:

接下来则来处理每一个Fragment的列表的显示处理了,由于我们之前已经对于列表的显示已经做了base的封装,所以说,实现起来也非常之容易。

准备Item bean:

package com.kotlin.musicplayer.modeldata class MvPagerBean(val data: List<VideosBean>
)data class VideosBean(val artistId: Int,val artistName: String,val briefDesc: Any,val cover: String,val desc: Any,val duration: Int,val id: Int,val mark: Int,val name: String,val playCount: Int,val subed: Boolean,val videoUrl: String = "http://vodkgeyttp8.vod.126.net/cloudmusic/obj/core/9337503943/e78b28855251f5c900771871537e6ae4.mp4?wsSecret=df2c6d3b9146c33d48b18840923a27f5&wsTime=1629178848"
)

其中MV地址也是写死了,来自林俊杰-裹着心的光。

准备Item View:

package com.kotlin.musicplayer.widgetimport android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.RelativeLayout
import com.itheima.player.model.bean.VideosBean
import com.kotlin.musicplayer.R
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.item_mv.view.*/*** mv每一个界面条目view*/
class MvItemView : RelativeLayout {constructor(context: Context?) : super(context)constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr)init {View.inflate(context, R.layout.item_mv, this)}/*** 适配每一个条目view*/fun setData(data: VideosBean) {//歌手名称artist.text = data.artistName//歌曲名称title.text = data.name//背景图Picasso.get().load(data.cover).into(bg)}
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"app:cardCornerRadius="5dp"app:cardElevation="5dp"app:cardUseCompatPadding="true"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="300dp"android:padding="10dp"><ImageViewandroid:id="@+id/bg"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop" /><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:maxLines="1"android:text="歌曲名称"android:textColor="#fff"android:textSize="20sp" /><TextViewandroid:id="@+id/artist"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/title"android:maxLines="1"android:text="歌手名称"android:textColor="#00ff00"android:textSize="25sp" /></RelativeLayout>
</androidx.cardview.widget.CardView>

准备列表Adapter:

package com.kotlin.musicplayer.adapterimport android.content.Context
import com.kotlin.musicplayer.base.BaseListAdapter
import com.kotlin.musicplayer.model.VideosBean
import com.kotlin.musicplayer.widget.MvItemView/*** mv界面每一个list列表的适配器*/
class MvListAdapter : BaseListAdapter<VideosBean, MvItemView>() {override fun refreshItemView(itemView: MvItemView, data: VideosBean) {itemView.setData(data)}override fun getItemView(context: Context?): MvItemView {return MvItemView(context)}
}

准备P、V层:

package com.kotlin.musicplayer.viewimport com.kotlin.musicplayer.base.BaseView
import com.kotlin.musicplayer.model.MvPagerBeaninterface MvListView : BaseView<MvPagerBean> {
}
package com.kotlin.musicplayer.presenter.`interface`import com.kotlin.musicplayer.base.BaseListPresenterinterface MvListPresenter : BaseListPresenter {
}
package com.kotlin.musicplayer.presenter.implimport com.kotlin.musicplayer.model.MvPagerBean
import com.kotlin.musicplayer.net.ResponseHandler
import com.kotlin.musicplayer.presenter.`interface`.MvListPresenter
import com.kotlin.musicplayer.view.MvListViewclass MvListPresenterImpl(var code: String, var mvListView: MvListView?) : MvListPresenter,ResponseHandler<MvPagerBean> {override fun onError(type: Int, msg: String?) {mvListView?.onError(msg)}override fun loadDatas() {//TODO}override fun loadMoreDatas(offset: Int) {TODO("Not yet implemented")}override fun onSuccessed(type: Int, result: MvPagerBean) {TODO("Not yet implemented")}override fun destoryView() {if (mvListView != null) {mvListView = null}}
}

准备列表接口请求:

package com.kotlin.musicplayer.netimport com.kotlin.musicplayer.model.MvPagerBean
import com.kotlin.musicplayer.utils.URLProviderUtils/*** mv每一个界面数据网络请求*/
class MvListRequest(type: Int, code: String, offset: Int, handler: ResponseHandler<MvPagerBean>) :MRequest<MvPagerBean>(type, URLProviderUtils.getMVListUrl(code, offset, 5), handler) {
}

其中url为:

实现MvListPresenterImpl:

package com.kotlin.musicplayer.presenter.implimport com.kotlin.musicplayer.base.BaseListPresenter
import com.kotlin.musicplayer.model.MvPagerBean
import com.kotlin.musicplayer.net.MvListRequest
import com.kotlin.musicplayer.net.ResponseHandler
import com.kotlin.musicplayer.presenter.`interface`.MvListPresenter
import com.kotlin.musicplayer.view.MvListViewclass MvListPresenterImpl(var code: String, var mvListView: MvListView?) : MvListPresenter,ResponseHandler<MvPagerBean> {override fun onError(type: Int, msg: String?) {mvListView?.onError(msg)}override fun loadDatas() {MvListRequest(BaseListPresenter.TYPE_INIT_OR_REFRESH, code, 0, this).excute()}override fun loadMoreDatas(offset: Int) {MvListRequest(BaseListPresenter.TYPE_LOAD_MORE, code, offset, this).excute()}override fun destoryView() {if (mvListView != null) {mvListView = null}}override fun onSuccessed(type: Int, result: MvPagerBean) {if (type == BaseListPresenter.TYPE_INIT_OR_REFRESH) {mvListView?.loadSuccessed(result)} else if (type == BaseListPresenter.TYPE_LOAD_MORE) {mvListView?.onLoadMore(result)}}
}

实现MvPagerFragment:

package com.kotlin.musicplayer.ui.fragmentimport com.kotlin.musicplayer.adapter.MvListAdapter
import com.kotlin.musicplayer.base.BaseListAdapter
import com.kotlin.musicplayer.base.BaseListFragment
import com.kotlin.musicplayer.base.BaseListPresenter
import com.kotlin.musicplayer.model.MvPagerBean
import com.kotlin.musicplayer.model.VideosBean
import com.kotlin.musicplayer.presenter.impl.MvListPresenterImpl
import com.kotlin.musicplayer.view.MvListView
import com.kotlin.musicplayer.widget.MvItemView/*** mv界面每一个页面fragment*/
class MvPagerFragment : BaseListFragment<MvPagerBean, VideosBean, MvItemView>(), MvListView {var tinguid: String? = nulloverride fun init() {tinguid = arguments?.getString("args")}override fun getSpecialAdapter(): BaseListAdapter<VideosBean, MvItemView> {return MvListAdapter()}override fun getSpecialPresenter(): BaseListPresenter {return MvListPresenterImpl(tinguid!!, this)}override fun getList(response: MvPagerBean?): List<VideosBean>? {return response?.songlist}}

运行:

实测过程中有可能会遇到接口返回500的情况:

这个没办法,人家服务器的事,咱们这边加上加载失败的处理就成,如下:

关注个人公众号,获得实时推送

 

这篇关于Kotlin项目实战之手机影音---加载mv界面区域数据、mv界面viewpager适配、tablayout适配、mv每一个界面列表显示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/798014

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作