悦单条目自定义及界面适配:
阐述:
距离上一次https://www.cnblogs.com/webor2006/p/13193142.html这块的学习已经相隔快半年了。。基本也把它快要遗忘在脑海里了,好在之前还有备忘可以复习一下,准备继续前行,不能放弃。上一次已经实现了首页这个TAB页面了,这次照理按顺序应该来实现MV这个TAB了:
但是这里从最后一个悦单Tab开始实现:
为什么呢?因为它里面长得跟首页Tab差不多,这里先来看一下它的预览效果:
由于比较简单,这里就快速来实现一下,顺便过一遍上一次实现的首页Tab当时构建的逻辑。
设置布局:
由于它的布局跟首页Tab是一模一样的,所以可以直接复用:
但是很显然这样写不太好,fragment_home是HomeFragment的嘛,所以有必要改一个名称,改成通用一点的:
准备列表Item布局:
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="5dp" app:cardElevation="5dp" app:cardUseCompatPadding="true"> <RelativeLayout android:layout_width="match_parent" android:layout_height="300dp" android:padding="10dp"> <ImageView android:id="@+id/img_bg" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/default_splash_bg" android:scaleType="centerCrop" /> <ImageView android:id="@+id/img_author_image" android:layout_width="60dp" android:layout_height="60dp" android:layout_alignParentBottom="true" android:layout_margin="10dp" tools:src="@color/colorAccent" /> <TextView android:id="@+id/tv_publishtime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginBottom="10dp" android:layout_toRightOf="@id/img_author_image" android:maxLines="1" android:textColor="#fff" android:textSize="20sp" tools:text="2020-12-20" /> <TextView android:id="@+id/tv_author_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/tv_publishtime" android:layout_toRightOf="@id/img_author_image" android:maxLines="1" android:textColor="#fff" android:textSize="20sp" tools:text="张三" /> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/tv_author_name" android:layout_toRightOf="@id/img_author_image" android:maxLines="1" android:textColor="#fff" android:textSize="20sp" tools:text="test" /> </RelativeLayout> </androidx.cardview.widget.CardView>
其预览效果长这样:
其中标红的是一个平常写布局的小小建议:为了能看到布局的效果不要直接将假的数据写到View当中了【比如TextView中的android:text】,而要使用tools标签来进行预览数据的模拟【比如TextView中的tools:text】,这样可以避免在线上可能会看到一些不想看到的假数据了。
准备YueDanItemView来加载item布局:
如之前首页Tab的实现,对于每个条目都会将其封装成一个View对象,避免在Adapter中出现有一堆设置数据的细节,增加代码可维护性,也是通常的技法,不多说:
package com.kotlin.musicplayer.ui.widget import android.content.Context import android.util.AttributeSet import android.view.View import android.widget.RelativeLayout import com.kotlin.musicplayer.R /** * 悦单界面每个条目的自定义view */ class YueDanItemView : 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_yuedan, this) } }
其中这里挪一下包:
这样看起来包体结构更合理一点。
准备Adpater:
它的写法跟HomeAdpater类似,先来回忆一下之前首页Tab的Adapter写法:
package com.kotlin.musicplayer.adapter import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.itheima.player.model.bean.HomeItemBean import com.kotlin.musicplayer.widget.HomeItemView import com.kotlin.musicplayer.widget.LoadMoreView /** * 首页列表Adapter */ class HomeAdapter : RecyclerView.Adapter<HomeAdapter.HomeHolder>() { private var list = ArrayList<HomeItemBean>() fun setData(list: List<HomeItemBean>?) { list?.let { this.list.clear() this.list.addAll(it) notifyDataSetChanged() } } fun loadMoreData(list: List<HomeItemBean>?) { list?.let { this.list.addAll(it) notifyDataSetChanged() } } class HomeHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeHolder { if (viewType == 1) { //最后一条 return HomeHolder(LoadMoreView(parent?.context)) } else { //普通条目 return HomeHolder(HomeItemView(parent?.context)) } } override fun getItemViewType(position: Int): Int { if (position == list.size) { //最后一条,则显示加载更多 return 1 } else { //普通条目 return 0 } } override fun getItemCount(): Int { return list.size + 1 } override fun onBindViewHolder(holder: HomeHolder, position: Int) { //如果是最后一条 不需要刷新view if (position == list.size) return val data = list.get(position) val itemView = holder.itemView as HomeItemView itemView.setData(data) } }
那不简单,“拷贝”再改吧改吧【注意:这个拷贝也足以看出代码的问题了,也就是逻辑相同但是实现还得重头来一次,这些就是之后代码重构的关键】,这里Adapter中先写死,先让其条目能正常显示出来既可:
package com.kotlin.musicplayer.adapter import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.kotlin.musicplayer.widget.YueDanItemView /** * 悦单列表Adapter */ class YuedanAdapter : RecyclerView.Adapter<YuedanAdapter.YuedanHolder>() { class YuedanHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): YuedanHolder { return YuedanHolder(YueDanItemView(parent?.context)) } override fun getItemCount(): Int { return 20 } override fun onBindViewHolder(holder: YuedanHolder, position: Int) { } }
绑定Adapter,看看初步效果:
运行:
加载悦单列表数据刷新列表:
接下来则来绑定真实的列表数据,其代码编写还是按之前的MVP风格来。
定义V层:
关于它里面方法的定义在之后再来完善,先建个空壳。
定义P层:
如之前首页Tab一样,也是需要定义一个接口,一个具体实现:
里面先空实现,待之后再慢慢填充:
package com.kotlin.musicplayer.presenter.`interface` interface YueDanPresenter { }
package com.kotlin.musicplayer.presenter.impl import com.kotlin.musicplayer.presenter.`interface`.YueDanPresenter import com.kotlin.musicplayer.view.YueDanView import java.lang.ref.WeakReference class YueDanPresenterImpl(val yueDanView: WeakReference<YueDanView>) : YueDanPresenter { }
封装网络请求:
还是参照之前首页Tab网络请求的封装:
其中发送请求得借助NetManager这个管理类,而具体每个模块则需要定义一个具体的Request,如HomeRequest,这里先来回忆一下当时HomeRequest里面是如何写的:
package com.kotlin.musicplayer.net import com.kotlin.musicplayer.model.HomeBean import com.kotlin.musicplayer.utils.URLProviderUtils class HomeRequest(type: Int, offset: Int, responseHandler: ResponseHandler<HomeBean>) : MRequest<HomeBean>(type, URLProviderUtils.getHomeUrl(offset, 5), responseHandler) { }
所以对照着也建议一个悦单相关的:
YueDanBean.kt:
package com.kotlin.musicplayer.model /** * 悦单列表bean */ data class YueDanBean( val songlist: List<YueDanItemBean> ) data class YueDanItemBean( val albums_total: String, val aliasname: String, val area: String, val artist_id: String, val avatar_big: String, val avatar_middle: String, val avatar_mini: String, val avatar_s1000: String, val avatar_s180: String, val avatar_s500: String, val avatar_small: String, val birth: String, val bloodtype: String, val company: String, val constellation: String, val country: String, val del_status: String, val firstchar: String, val gender: String, val intro: String, val name: String, val piao_id: String, val songs_total: String, val source: String, val stature: String, val ting_uid: String, val translatename: String, val url: String, val weight: String, val title: String, val author: String, val pic_small: String, val album_500_500: String, val hot: String, val publishtime: String )
YueDanRequest.kt:
package com.kotlin.musicplayer.net import com.kotlin.musicplayer.model.YueDanBean import com.kotlin.musicplayer.utils.URLProviderUtils /** * 悦单界面网络请求request */ class YueDanRequest(type: Int, offset: Int, handler: ResponseHandler<YueDanBean>) : MRequest<YueDanBean>(type, URLProviderUtils.getYueDanUrl(offset, 20), handler) { }
其中URLProviderUtils.getYueDanUrl()为:
/** * 获取悦单列表的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; }
【说明】:上面的URL是网上自己找的,说不定哪天就不能用了,可以根据需要上网自己搜源~~
YueDanFragment.initData()中发起网络请求:
如之前首页Tab一样,发起网络请求是这样写的:
所以依葫芦画瓢:
此时回到P层定义一下,先定义接口,其里面的方法跟HomeView的一模一样【注意:这块肯定未来要封装的,不然这种来回copy的方式太low了,也不太好维护】:
copy到YueDanPresenter中:
此时就可以回到具体的p类中来实现了:
package com.kotlin.musicplayer.presenter.impl import com.kotlin.musicplayer.model.YueDanBean import com.kotlin.musicplayer.net.ResponseHandler import com.kotlin.musicplayer.net.YueDanRequest import com.kotlin.musicplayer.presenter.`interface`.YueDanPresenter import com.kotlin.musicplayer.ui.fragment.YueDanFragment import java.lang.ref.WeakReference class YueDanPresenterImpl(val yueDanView: WeakReference<YueDanFragment>) : YueDanPresenter, ResponseHandler<YueDanBean> { override fun loadDatas() { YueDanRequest(YueDanPresenter.TYPE_INIT_OR_REFRESH, 0, this).excute() } override fun loadMoreDatas(offset: Int) { TODO("Not yet implemented") } override fun onError(type: Int, msg: String?) { TODO("Not yet implemented") } override fun onSuccessed(type: Int, result: YueDanBean) { TODO("Not yet implemented") } }
其中请求成功与失败的回调肯定得要调用V层的方法,但是呢目前咱们的YueDanView里面方法还没有定义,所以这里也来完善一下,完善方式来是校仿HomeView的copy大法:
package com.kotlin.musicplayer.view import com.kotlin.musicplayer.model.YueDanBean interface YueDanView { /** * 获取数据失败 */ fun onError(message: String?) /** * 初始化数据或者刷新数据成功 */ fun loadSuccessed(reponse: YueDanBean?) /** * 加载更多成功 */ fun loadMore(response: YueDanBean?) }
此时就可以回到P层中请求回调中调用V中的方法了:
此时回到Fragment中就可以发起网络请求了:
处理数据加载成功刷新Adapter:
此时就需要给YuedanAdapter中增加刷新数据的方法,如下:
悦单条目view初始化:
有了数据之后,接下来则对列表条目进行数据初始化,直接贴代码:
运行看一下:
不过目前头像不是圆角的,所以接下来处理一下,关于Picasso的圆角处理可以参考大佬的文章https://blog.csdn.net/growing_tree/article/details/106618191,其实上跟Glide差不多,先添加依赖:
implementation 'jp.wasabeef:picasso-transformations:2.1.2'
然后添加一个transform配置既可:
运行:
悦单界面下拉刷新和上拉加载更多:
下拉刷新:
这块也是跟首页Tab逻辑类似。
运行看一下:
上拉加载更多:
这块跟首页Tab逻辑也一样~~
修改Adapter:
为了有一个分页loading的效果,所以对于整个列表项而言得多出一项,所以先来将getCount()加1:
然后需要定义getItemType()了,如下:
override fun getItemViewType(position: Int): Int { if (position == list.size) { //最后一条,则显示加载更多 return 1 } else { //普通条目 return 0 } }
接下来对应的得来修改onCreateViewHolder()了,得根据列表类型绑不同的View:
最后再来修改一下onBindViewHolder():
package com.kotlin.musicplayer.adapter import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.kotlin.musicplayer.model.YueDanItemBean import com.kotlin.musicplayer.widget.HomeItemView import com.kotlin.musicplayer.widget.LoadMoreView import com.kotlin.musicplayer.widget.YueDanItemView /** * 悦单列表Adapter */ class YuedanAdapter : RecyclerView.Adapter<YuedanAdapter.YuedanHolder>() { private var list = ArrayList<YueDanItemBean>() fun setData(list: List<YueDanItemBean>?) { list?.let { this.list.clear() this.list.addAll(it) notifyDataSetChanged() } } class YuedanHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): YuedanHolder { if (viewType == 1) { //最后一条 return YuedanHolder(LoadMoreView(parent?.context)) } else { //普通条目 return YuedanHolder(YueDanItemView(parent?.context)) } } override fun getItemCount(): Int { return list.size + 1 } override fun getItemViewType(position: Int): Int { if (position == list.size) { //最后一条,则显示加载更多 return 1 } else { //普通条目 return 0 } } override fun onBindViewHolder(holder: YuedanHolder, position: Int) { //如果是最后一条 不需要刷新view if (position == list.size) return val yueDanItemBean = list.get(position) val yueDanItemView = holder?.itemView as YueDanItemView yueDanItemView.setData(yueDanItemBean) } }
设置滑动监听:
这里有一个体现Kotlin比较人性的地方需要提示一下,就是它的智能类型转换,啥意思?
YueDanPresenterImpl.loadMoreDatas()实现:
接下来则回到Fragment中来处理加载更多:
然后在Adapter中来处理加载更多:
下面来运行看一下:
抽取BaseListFragment:
在上面的实现中,一直在啰嗦提醒一句话:“跟首页tab的功能差不多”, 如果在实际项目的开发中经常会看到跟某某界面的功能相似,对于有代码追求的开发者来说此时抽取Base就变得很有必要了,一是可以大大加快开发效率,二是也能避免大量copy造成的一些出错,最重要的一点是心情爽呀,谁愿意同样的功能重复性的写N遍呢?所以接下来咱们针对已经实现的首页和悦单Tab进行一个base提取,也为之后的MV的tab实现打好一个非常好的基础。
基类抽取思路:
抽取点:
接下来先来捋一下需要抽取的点,其实也就是看首页和悦单两者之间哪些是有共同性的,这里从2个层面来进行。
View:
- 列表显示
- 下拉刷新
- 上拉加载
data:
- 初始化数据
- 刷新数据
- 加载更多数据
抽取方法:
最笨的抽取方法就是先新建一个Base,然后将首页或者悦单的全面代码拷至里面,然后再一点点将共同的功能保留,不共同的则由具体子类来实现既可。
抽取view以及presenter和adapter的基类:
新建Base基类:
copy具体页面的实现到base:
这里以HomeFragment为例将它的所有实现拷进来:
挼出不通用的点:
接下来要抽取的核心就是将不通用的点让子类来实现,其实咱们这页面比较简单很容易就挼出来了,而对于复杂页面可能需要花些时间的,不过都不难,下面先来挼一挼:
HomeView:
它应该是一个通用的才行,可以看一下HomeView里面定义的内容:
其实抽取一个BaseView就可以了,这三个方法应该是每个列表页面都需要的,其中HomeBean就可以用一个泛型来定义。
HomeAdapter:
而它里面也得将一些具体的类型给泛化掉:
HomePresenterImpl:
这些出现了具体的地方全得通用化,其实也就应该是要抽取对应的Base出来才行,其实总的来说是需要这样抽取:
所以下面开始来抽取一下。
实现BaseListFragment的抽取:
将HomeView的内容抽取到BaseView中:
但是呢,这里的HomeBean很显然得用泛型了,因为这里面是通用的行为:
此时咱们的HomeView和YueDanView就可以继承至它了:
目前由于这俩tab模块的行为跟BaseView一样,所以继承之后里面就是空空的了,但是!!!未来如果有模块有新的行为则可以自行扩展。
将HomePresenter的内容提取到BaseListPresenter中:
package com.kotlin.musicplayer.base /** * 所有下拉刷新和上拉加载更多列表界面presenter的基类 */ interface BaseListPresenter { companion object { val TYPE_INIT_OR_REFRESH = 1 val TYPE_LOAD_MORE = 2 } fun loadDatas() fun loadMoreDatas(offset: Int) }
其中还可以扩展一个方法,如下:
关于它这块的具体实现之后再说,先扩展一下。此时就可以把HomePresenter和YueDanPresenter继承至它了:
修复具体Presenter的报错:
由于将原Presenter定义的都抽取到BaseListPresenter,所以对于HomePresenterImpl和YueDanPresenterImpl就会报错了,下面来解除一下:
HomePresenterImpl:
YueDanPresenterImpl:
这个是同样的,直接贴出来:
package com.kotlin.musicplayer.presenter.impl import com.kotlin.musicplayer.base.BaseListPresenter import com.kotlin.musicplayer.model.YueDanBean import com.kotlin.musicplayer.net.ResponseHandler import com.kotlin.musicplayer.net.YueDanRequest import com.kotlin.musicplayer.presenter.`interface`.YueDanPresenter import com.kotlin.musicplayer.view.YueDanView import java.lang.ref.WeakReference class YueDanPresenterImpl(val yueDanView: WeakReference<YueDanView>) : YueDanPresenter, ResponseHandler<YueDanBean> { override fun loadDatas() { YueDanRequest(BaseListPresenter.TYPE_INIT_OR_REFRESH, 0, this).excute() } override fun loadMoreDatas(offset: Int) { YueDanRequest(BaseListPresenter.TYPE_LOAD_MORE, offset, this).excute() } override fun onError(type: Int, msg: String?) { yueDanView.get()?.onError(msg) } override fun onSuccessed(type: Int, result: YueDanBean) { when (type) { BaseListPresenter.TYPE_INIT_OR_REFRESH -> yueDanView.get()?.loadSuccessed(result) BaseListPresenter.TYPE_LOAD_MORE -> yueDanView.get()?.onLoadMore(result) } } override fun destoryView() { //TODO } }
将HomeAdapter的内容提取到BaseListAdapter中:
新建BaseListAdapter:
将HomeAdapter的内容拷到BaseListAdapter当中:
package com.kotlin.musicplayer.base import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.kotlin.musicplayer.adapter.HomeAdapter import com.kotlin.musicplayer.model.HomeItemBean import com.kotlin.musicplayer.widget.HomeItemView import com.kotlin.musicplayer.widget.LoadMoreView /** * 所有下拉刷新和上拉加载更多列表界面adapter基类 */ class BaseListAdapter : RecyclerView.Adapter<HomeAdapter.HomeHolder>() { private var list = ArrayList<HomeItemBean>() fun setData(list: List<HomeItemBean>?) { list?.let { this.list.clear() this.list.addAll(it) notifyDataSetChanged() } } fun loadMoreData(list: List<HomeItemBean>?) { list?.let { this.list.addAll(it) notifyDataSetChanged() } } class HomeHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeAdapter.HomeHolder { if (viewType == 1) { //最后一条 return HomeAdapter.HomeHolder(LoadMoreView(parent?.context)) } else { //普通条目 return HomeAdapter.HomeHolder(HomeItemView(parent?.context)) } } override fun getItemViewType(position: Int): Int { if (position == list.size) { //最后一条,则显示加载更多 return 1 } else { //普通条目 return 0 } } override fun getItemCount(): Int { return list.size + 1 } override fun onBindViewHolder(holder: HomeAdapter.HomeHolder, position: Int) { //如果是最后一条 不需要刷新view if (position == list.size) return val data = list.get(position) val itemView = holder.itemView as HomeItemView itemView.setData(data) } }
通用化改造:
1、将HomeHolder改为通用的:
2、将HomeItemBean泛型化:
3、 将HomeItemView泛型化:
4、将setData抽象化:
5、将onCreateViewHolder()中的创建HomeItemView抽象化:
让HomeAdapter、YuedanAdapter继承至BaseListAdapter:
有了抽象Adapter的封装之后,具体子类实现就超级清爽了,下面来改造一下:
package com.kotlin.musicplayer.adapter import android.content.Context import com.kotlin.musicplayer.base.BaseListAdapter import com.kotlin.musicplayer.model.HomeItemBean import com.kotlin.musicplayer.widget.HomeItemView /** * 首页列表Adapter */ class HomeAdapter : BaseListAdapter<HomeItemBean, HomeItemView>() { override fun refreshItemView(itemView: HomeItemView, data: HomeItemBean) { itemView.setData(data) } override fun getItemView(context: Context?): HomeItemView { return HomeItemView(context) } }
package com.kotlin.musicplayer.adapter import android.content.Context import com.kotlin.musicplayer.base.BaseListAdapter import com.kotlin.musicplayer.model.YueDanItemBean import com.kotlin.musicplayer.widget.YueDanItemView /** * 悦单列表Adapter */ class YuedanAdapter : BaseListAdapter<YueDanItemBean, YueDanItemView>() { override fun refreshItemView(itemView: YueDanItemView, data: YueDanItemBean) { itemView.setData(data) } override fun getItemView(context: Context?): YueDanItemView { return YueDanItemView(context) } }
BaseListFragment通用化改造:
接下来则可以回头来改造咱们之前所创建的BaseListFragment。
将HomeView通用化:
将loadSuccessed()、onLoadMore()处理数据回调泛型化:
而每个数据中获取列表的方式可能是不一样的,所以交由子类来处理:
将HomeAdapter通用化:
而它的创建也由子类来实现:
将HomePresenterImpl通用化:
同样的也是交由子类来实现:
package com.kotlin.musicplayer.base import android.graphics.Color import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.kotlin.musicplayer.R import kotlinx.android.synthetic.main.fragment_list.* /** * 所有具有下拉刷新和上拉加载更多列表界面的基类 * HomeView->BaseView * Presenter->BaseListPresenter * Adapter->BaseListAdapter */ abstract class BaseListFragment<RESPONSE, ITEMBEAN, ITEMVIEW : View> : BaseFragment(), BaseView<RESPONSE> { val adapter by lazy { getSpecialAdapter() } val homePresenterImpl by lazy { getSpecialPresenter() } override fun initView(): View? { return View.inflate(context, R.layout.fragment_list, null) } override fun initListeners() { super.initListeners() recyclerView.layoutManager = LinearLayoutManager(context) recyclerView.adapter = adapter //监听列表滑动 recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { //是否最后一条已经显示 val layoutManager = recyclerView.layoutManager if (layoutManager is LinearLayoutManager) { //由于RecycleView还有其它样式的列表,所以这里只有下拉列表类型才处理分页 val manager: LinearLayoutManager = layoutManager val lastPosition = manager.findLastVisibleItemPosition() if (lastPosition == adapter.itemCount - 1) { //开始加载更多数据 homePresenterImpl.loadMoreDatas(adapter.itemCount - 1); } } } } }) lay_refresh.setColorSchemeColors(Color.RED, Color.YELLOW, Color.BLUE) lay_refresh.setOnRefreshListener { homePresenterImpl.loadDatas() } } override fun initData() { super.initData() homePresenterImpl.loadDatas() } override fun onError(message: String?) { showToast("获取数据出错") lay_refresh.isRefreshing = false } override fun loadSuccessed(response: RESPONSE?) { lay_refresh.isRefreshing = false adapter.setData(getList(response)) } override fun onLoadMore(response: RESPONSE?) { lay_refresh.isRefreshing = false adapter.loadMoreData(getList(response)) } /** * 获取适配器adapter */ abstract fun getSpecialAdapter(): BaseListAdapter<ITEMBEAN, ITEMVIEW> /** * 获取presenter */ abstract fun getSpecialPresenter(): BaseListPresenter /** * 从返回结果中获取列表数据集合 */ abstract fun getList(response: RESPONSE?): List<ITEMBEAN>? }
至此整个BaseListFragment就已经抽取完了。
让HomeFragemnt、YueDanAdapter继承至BaseListFragment:
同样是有了抽象之后,子类的实现就变得异常的舒适简单了。
package com.kotlin.musicplayer.ui.fragment import com.kotlin.musicplayer.adapter.HomeAdapter import com.kotlin.musicplayer.base.BaseListAdapter import com.kotlin.musicplayer.base.BaseListFragment import com.kotlin.musicplayer.base.BaseListPresenter import com.kotlin.musicplayer.model.HomeBean import com.kotlin.musicplayer.model.HomeItemBean import com.kotlin.musicplayer.presenter.impl.HomePresenterImpl import com.kotlin.musicplayer.widget.HomeItemView import java.lang.ref.WeakReference /** * 首页 */ class HomeFragment : BaseListFragment<HomeBean, HomeItemBean, HomeItemView>() { override fun getSpecialAdapter(): BaseListAdapter<HomeItemBean, HomeItemView> { return HomeAdapter() } override fun getSpecialPresenter(): BaseListPresenter { return HomePresenterImpl(WeakReference(this)) } override fun getList(response: HomeBean?): List<HomeItemBean>? { return response?.songlist } }
其中HomePresenterImpl中的这块需要改一下:
因为基类抽象化了嘛,同样的对于YueDanFragment也一样:
package com.kotlin.musicplayer.ui.fragment import com.kotlin.musicplayer.adapter.YuedanAdapter import com.kotlin.musicplayer.base.BaseListAdapter import com.kotlin.musicplayer.base.BaseListFragment import com.kotlin.musicplayer.base.BaseListPresenter import com.kotlin.musicplayer.model.YueDanBean import com.kotlin.musicplayer.model.YueDanItemBean import com.kotlin.musicplayer.presenter.impl.YueDanPresenterImpl import com.kotlin.musicplayer.widget.YueDanItemView import java.lang.ref.WeakReference /** * 悦单 */ class YueDanFragment : BaseListFragment<YueDanBean, YueDanItemBean, YueDanItemView>() { override fun getSpecialAdapter(): BaseListAdapter<YueDanItemBean, YueDanItemView> { return YuedanAdapter() } override fun getSpecialPresenter(): BaseListPresenter { return YueDanPresenterImpl(WeakReference(this)) } override fun getList(response: YueDanBean?): List<YueDanItemBean>? { return response?.songlist } }
最后挂接destroy()方法:
最后还有一个小细节需要处理:
这块还没调用,虽说目前都是空实现,但是有可能在之后会在销毁做一些处理嘛,所以:
至此整个抽取工作完成,之后再实现类似列表的页面就可以秒秒钟搞定了,而且还好维护~~最后温馨提示:任何重构都得细测,这是对软件的一种尊重~~不过这里仅是为了学习,能正常运行就成。