距离上一次Kotlin项目实战之手机影音---悦单条目实现及baseListFragment抽取的这个Kotlin项目的学习间隔一年多了,完成已经消失在自己的记忆当中了,当然不能半途而废,继续接着前行,然后运行把工程重新运行一下,发现项目起不来了,尴尬。。
原因是由于音乐的API的域名访问不了了。。其实也正常,因为在之前Kotlin项目实战之手机影音---主界面tab切换、home界面适配、获得首页网络数据就说音乐数据是从网上找的,比如进首页的api为:
然后到浏览器访问一下:
寻找新的网上音乐API:只能另找一些免费的数据源了【如果之后又被封了,我打算自己搭建个后台mock数据得了,要不用node服务器,要不用springboot,总之办法大于困难~~】,在网上搜到个这个 网易云音乐API_余光的博客-CSDN博客_免费音乐api">免费稳定 => 网易云音乐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 {
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;
}
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
data class HomeBean(
val data: List
)
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
data class YueDanBean(
val data: List
)
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
data class HomeBean(
val data: List
)
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
data class YueDanBean(
val data: List
)
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组件,咱们这里来使用它,如下:
加载tab音乐分类数据:
很显然对于上面的tab是从网络上来获取的,这边正好也有现成的接口:
创建MVView接口:该接口是用来实现P层数据回调给UI层的,这里先新建一下,里面的接口方法之后用到时再来定义:
package com.kotlin.musicplayer.view
import com.kotlin.musicplayer.model.MVTag
interface MVView {
fun onError(msg: String?)
fun onSuccess(result: List)
}
其中这里不像之前的页面一样去继承baseView了:
因为没有下拉刷新列表动作,然后涉及到一个model:
package com.kotlin.musicplayer.model
data class MvAreaBean(
val tags: List
)
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.impl
import com.kotlin.musicplayer.model.MvAreaBean
import com.kotlin.musicplayer.net.ResponseHandler
import com.kotlin.musicplayer.presenter.`interface`.MvPresenter
import com.kotlin.musicplayer.view.MVView
class MvPresenterImpl(var mvView: MVView) : MvPresenter, ResponseHandler {
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.net import com.kotlin.musicplayer.model.MvAreaBean import com.kotlin.musicplayer.utils.URLProviderUtils class MvAreaRequest(handler: ResponseHandler) : MRequest (0, URLProviderUtils.getMVareaUrl(), handler) { }
然后在界面简单处理一下结果回调,目前先不处理列表显示,只先打个toast出来,先确保接口是畅通的:
运行: mv界面viewpager适配: 准备PagerAdapter:package com.kotlin.musicplayer.adapter import 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 class MvPagerAdapter(val list: List?, 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 = bundle return 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.model
data class MvPagerBean(
val data: List
)
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.widget
import 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.*
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)
}
fun setData(data: VideosBean) {
//歌手名称
artist.text = data.artistName
//歌曲名称
title.text = data.name
//背景图
Picasso.get().load(data.cover).into(bg)
}
}
布局文件:
准备列表Adapter:
package com.kotlin.musicplayer.adapter import android.content.Context import com.kotlin.musicplayer.base.baseListAdapter import com.kotlin.musicplayer.model.VideosBean import com.kotlin.musicplayer.widget.MvItemView class MvListAdapter : baseListAdapter准备P、V层:() { override fun refreshItemView(itemView: MvItemView, data: VideosBean) { itemView.setData(data) } override fun getItemView(context: Context?): MvItemView { return MvItemView(context) } }
package com.kotlin.musicplayer.view import com.kotlin.musicplayer.base.baseView import com.kotlin.musicplayer.model.MvPagerBean interface MvListView : baseView准备列表接口请求:{ } package com.kotlin.musicplayer.presenter.`interface` import com.kotlin.musicplayer.base.baseListPresenter interface MvListPresenter : baseListPresenter { } package com.kotlin.musicplayer.presenter.impl import com.kotlin.musicplayer.model.MvPagerBean import com.kotlin.musicplayer.net.ResponseHandler import com.kotlin.musicplayer.presenter.`interface`.MvListPresenter import com.kotlin.musicplayer.view.MvListView class MvListPresenterImpl(var code: String, var mvListView: MvListView?) : MvListPresenter, ResponseHandler { 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.net import com.kotlin.musicplayer.model.MvPagerBean import com.kotlin.musicplayer.utils.URLProviderUtils class MvListRequest(type: Int, code: String, offset: Int, handler: ResponseHandler) : MRequest (type, URLProviderUtils.getMVListUrl(code, offset, 5), handler) { }
其中url为:
实现MvListPresenterImpl:package com.kotlin.musicplayer.presenter.impl
import 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.MvListView
class MvListPresenterImpl(var code: String, var mvListView: MvListView?) : MvListPresenter,
ResponseHandler {
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.fragment import 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 class MvPagerFragment : baseListFragment运行:(), MvListView { var tinguid: String? = null override fun init() { tinguid = arguments?.getString("args") } override fun getSpecialAdapter(): baseListAdapter { return MvListAdapter() } override fun getSpecialPresenter(): baseListPresenter { return MvListPresenterImpl(tinguid!!, this) } override fun getList(response: MvPagerBean?): List ? { return response?.songlist } }
实测过程中有可能会遇到接口返回500的情况:
这个没办法,人家服务器的事,咱们这边加上加载失败的处理就成,如下:
关注个人公众号,获得实时推送



