ListAdapter封装 (四) - 分页封装;

前言:

这是ListAdapter最后一篇了;  简单封装一下分页逻辑;  由于框架五花八门(网络请求, 下拉刷新, 加载更多等),这篇只做参照即可;

分析: 

ViewModel 需要处理:  页码, 请求解析数据,  发送加载更多状态, 发送下拉刷新状态等;

View 需要处理: 初始化Adapter, RecycleView. 绑定加载更多控件, 监听下拉及加载更多状态等;

本篇用到的库:

//loadmore
implementation 'com.github.nukc:loadmorewrapper:1.8.3' //retrofit 2.9 系列
implementation "com.squareup.retrofit2:retrofit:2.9.0" //viewmodel
implementation "androidx.lifecycle:lifecycle-viewmodel:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

1.BasePageModel;  

它是分页 ViewModel 的基类.  使用 retrofit 拉取数据;

abstract class BasePageModel<T: BaseItem> (application: Application) : AndroidViewModel(application){
private var pageInit = 1
private var page = pageInit
private var pageSize = 10
private var maxPage: Int = 1 /**
* 下拉刷新状态,
*/
var refreshEnabled: MutableLiveData<Boolean> = MutableLiveData(false) /**
* 加载更多状态
*/
var loadEnabled: MutableLiveData<Boolean> = MutableLiveData(true) /**
* 列表数据
*/
var listData: MutableLiveData<MutableList<T>> = MutableLiveData(mutableListOf()) fun loadData(param: ArrayMap<String, Any>){
param["page"] = page
param["pageSize"] = pageSize //发起网络请求.
retrofit<JsonObject>(viewModelScope) {
onSuccess {
disposeResp(it)
}
onFailed { msg, _ ->
Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()
}
onComplete {
refreshEnabled.value = false
} api = getResp(ApiManager.INSTANCE.getJsonBody(param))
}
} /**
* 处理响应;
* 1.解析响应数据; 2.获取总页码数; 3.拼接数据;
* 4.页码++ 5.判断loadMore Enabled 6.取消下拉刷新状态
*/
private fun disposeResp(resp: BaseResp<JsonObject?>){
val list = analysisData(resp.data) ?: mutableListOf()
maxPage = resp.data?.asJsonObject?.get("totalPage")?.asInt ?: 1 if (page == pageInit) {
listData.value = list
} else {
val data = listData.value
data?.addAll(list)
listData.value = data
}
page++ if (page > maxPage) {
switchEnabled(false)
} else {
//数据够,则继续加载; 数据不够了,则关闭加载
switchEnabled(list.size >= pageSize)
}
} /**
* 切换 loadMore Enabled;
*/
private fun switchEnabled(boolean: Boolean){
if(boolean != loadEnabled.value){
loadEnabled.value = boolean
}
} /**
* 重新加载数据;
*/
fun reload(){
page = pageInit
listData.value = mutableListOf()
switchEnabled(true)
} /**
* 处理数据, 因服务器响应数据不规范, 只能用Json接收;
* 可以省去 - 当响应数据可以直接解析为 实体集合时,
*/
abstract fun analysisData(jo: JsonObject?): MutableList<T>? /**
* retrofit API
*/
abstract suspend fun getResp(rBody: RequestBody): BaseResp<JsonObject?>
}

2. ViewModel

它继承 BasePageModel;

class CheckModel(application: Application) : BasePageModel<CheckEntity>(application) {

    override fun analysisData(jo: JsonObject?): MutableList<CheckEntity>? {
val str: String? = jo?.getAsJsonArray("newsList")?.toString()
//将String 解析为 实体集合
return str?.toBeanList()
} override suspend fun getResp(rBody: RequestBody): BaseResp<JsonObject?> {
return ApiManager.INSTANCE.mApi.getDynamicList(rBody)
} }

3. MyPageHelper

使用 loadmorewrapper 库 加载更多; 监听数据, 监听状态都写在这个类里

class MyPageHelper(
adapter: BaseAdapter<out BaseItem>,
private val mRecycle: RecyclerView,
private val pageModel: BasePageModel<out BaseItem>,
/**
* Activity 或 Fragment
* 1. 提供 Lifecycle
* 2. 提供 请求参数; 这里全是用 map -> JsonBody 方式提交参数;
*/
private val mView: PagingView
) {
private val mAdapter = adapter as BaseAdapter<BaseItem>
private var mEnabled: LoadMoreAdapter.Enabled? = null fun load(){
LoadMoreWrapper.with(mRecycle.adapter).apply{
setShowNoMoreEnabled(true) // enable show NoMoreView,default false
setListener { enabled: LoadMoreAdapter.Enabled ->
mEnabled = enabled val param = ArrayMap<String, Any>()
mView.prepareMap(param)
pageModel.loadData(param)
}
}.into(mRecycle) // 数据变更时, 刷新列表
pageModel.listData.observe(mView){
mAdapter.submitList(it)
} // 监听没有更多的状态;
// 当页码超出, 或当前页数据不满一页时, 没有更多
// 当页面下拉刷新时,重新启动 loadMore
pageModel.loadEnabled.observe(mView){
setEnabled(it)
}
} /**
* 控制加载更多功能; false: 显示没有更过; true: 滚动到底时触发 loadMore
*/
private fun setEnabled(enabled: Boolean) {
mEnabled?.let {
it.loadMoreEnabled = enabled
if(enabled){
mRecycle.adapter?.notifyItemChanged(mRecycle.adapter?.itemCount ?: 0)
}
}
} /**
* Activity 或 Fragment 需要实现它
*/
interface PagingView : LifecycleOwner {
/**
* 绑定额外请求参数;
*/
fun prepareMap(map: ArrayMap<String, Any>){}
}
}

4.使用

需要实现  MyPageHelper.PagingView;  重写 prepareMap() 提供请求参数

然后初始化 Adapter, RecycleView, Helper;

下拉刷新, 也可以写在 Helper 中;

class CheckPageFragment : BaseMVVMFragment<CheckModel, LayoutRecycleBinding>(),
MyPageHelper.PagingView { private lateinit var mAdapter: SingleChoiceAdapter<CheckEntity>
private var mHelper: MyPageHelper? = null override fun getLayoutId(): Int = R.layout.layout_recycle override fun onLazyLoad() {
//初始化 Adapter 及 RecycleView
mAdapter = SingleChoiceAdapter(R.layout.item_test_choise)
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
} //初始化 Helper
mViewModel?.let {
mHelper = MyPageHelper(mAdapter, mDataBind.rvRecycle, it, this)
mHelper?.load()
} //swipeRefresh
mDataBind.swipeRefresh.setOnRefreshListener {
mViewModel?.reload()
}
mViewModel?.refreshEnabled?.observe(this){
mDataBind.swipeRefresh.isRefreshing = it
}
}
}

回到顶部

孟老板 ListAdapter封装, 告别Adapter代码 (四)的更多相关文章

  1. 孟老板 ListAdapter封装, 告别Adapter代码 (三)

    BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...

  2. 孟老板 ListAdapter封装, 告别Adapter代码 (上)

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  3. 孟老板 BaseAdapter封装(五) ListAdapter

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  4. 孟老板 BaseAdapter封装(四) PageHelper

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  5. 孟老板 BaseAdapter封装 (一) 简单封装

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  6. 孟老板 BaseAdapter封装 (三) 空数据占位图

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  7. 孟老板 BaseAdapter封装 (二) Healer,footer

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  8. 孟老板 Paging3 (二) 结合Room

    BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (二)  结合Room Paging 数据源不开放, 无法 ...

  9. 孟老板 Paging3 (一) 入门

    BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (一)  入门 前言: 官方分页工具,  确实香.   但 ...

随机推荐

  1. 【apache】使用HttpClient,进行简单网页抓取

    1 package com.lw.httpclient.test; 2 import org.apache.http.client.methods.CloseableHttpResponse; 3 i ...

  2. 客户端保存token到sessionStorage

    将token保存到客户端的sessionStorage 一.区分localStorage和sessionStorage localStorage是本地持久化存储 sessionStorage是浏览器会 ...

  3. [bug] Authentication failed for token submission (认证失败)异常

    原因 gitee上下的项目,启动后能访问首页,但登录报错.原因是根据用户名上数据库查密码没有得到结果,中间任何环节有问题都可能导致,我的是因为mapper.xml中的<mapper namesp ...

  4. at在指定的时间执行命令+atq列出用户待处理作业(jobs)

    按下crtl+d取消定时任务 # at now+1hourat> echo"a">aat> <EOF>at> <EOT>job 4 ...

  5. 关于STM32的可编程电压检测器的使用方法

    关于STM32的可编程电压检测器的使用方法 思维导图总览: 代码: 1 #include "sys.h" 2 #include "delay.h" 3 #inc ...

  6. 为何使用thrift-rpc与http的选择

    在工作中偶然看到公司旧架构在loaclserver中使用的是thrift,遂记录一下 thrif作为一种rpc框架 接口描述语言和二进制通信协议,至于为何使用thrift 其问题本质是为何在已有htt ...

  7. System Verilog MCDF(二)

    整形器的接口时序: reg,grant是维持了两个clk的. chid ,length在发送数据期间不可以变化. 第一个data数据必须在start上升沿的同一个clk发送. reg,grant两者之 ...

  8. android设置时钟

    <TextClock        android:id="@+id/timeText"        android:layout_width="match_pa ...

  9. Docker学习(7) 构建镜像

    构建docker镜像 1 构建镜像的两种方式 1 通过容器构建镜像 2 通过Dockerfile构建镜像

  10. Python+Selenium学习笔记11 - python官网的tutorial - 定义函数

    1 def f(a, L=[]): 2 L.append(a) 3 return L 4 5 print f(5) 6 print f(2) 输出 1 def f(a, L=None): 2 if L ...