android 跨页回调,Android Paging+BoundaryCallback分页的实现与封装

ViewModel+LiveData Android Architecture Components

Paging Library Paging

Paging

1. Datasource

数据源抽象类,Paging有三种实现

(1)PageKeyedDataSource 按页加载,如请求数据时传入page页码。

(2)ItemKeyedDataSource 按条目加载,即请求数据需要传入其它item的信息,如加载第n+1项的数据需传入第n项的id。

(3)PositionalDataSource 按位置加载,如加载指定从第n条到n+20条。

2. PagedList

PagedList是List的子类,通过Datasource加载数据,并可以设置一次加载的数量以及预加载的数量等。

3.PagedListAdapter

PagedListAdapte是RecyclerView.Adapter的实现,用于展示PagedList的数据。数据源变动时后台线程通过DiffUtil比较前后两个PagedList的差异,然后调用notifyItem...()方法更新RecyclerView。

4. LivePagedListBuilder

将PagedList和LiveData整合成LiveData。

Paging+BoundaryCallback封装

网上许多demo都是基于google example,官方example主要是教我们怎么用,也有很多值得参考的地方,但直接搬项目中去用却明显水土不服,建议先理解了google example中Paging的用法,再自己封装下...

定义列表的几种状态

/**

* 列表状态

*/

sealed class ListStatus

/**

* 初始化中

*/

class Initialize : ListStatus()

/**

* 初始化成功

*/

class InitializeSuccess : ListStatus()

/**

* 初始化错误

*/

class InitializeError : ListStatus()

/**

* 列表空

*/

class Empty : ListStatus()

/**

* 列表往下加载更多中

*/

class LoadMoreIn : ListStatus()

/**

* 列表往下加载成功

*/

class LoadMoreSuccess : ListStatus()

/**

* 列表往下加载失败

*/

class LoadMoreError : ListStatus()

/**

* 列表往下已全部加载完毕

*/

class End : ListStatus()

/**

* 列表往上加载更多中

*/

class AtFrontLoadMoreIn : ListStatus()

/**

* 列表往上加载成功

*/

class AtFrontLoadMoreSuccess : ListStatus()

/**

* 列表往上加载失败

*/

class AtFrontLoadMoreError : ListStatus()

/**

* 列表往上已全部加载完毕

*/

class AtFrontEnd : ListStatus()

自定义BoundaryCallback

class ListBoundaryCallback(private val listLiveData: MutableLiveData) : PagedList.BoundaryCallback() {

override fun onZeroItemsLoaded() {

super.onZeroItemsLoaded()

listLiveData.value = Empty()

}

override fun onItemAtEndLoaded(itemAtEnd: Value) {

super.onItemAtEndLoaded(itemAtEnd)

listLiveData.value = End()

}

override fun onItemAtFrontLoaded(itemAtFront: Value) {

super.onItemAtFrontLoaded(itemAtFront)

listLiveData.value = AtFrontEnd()

}

}

BoundaryCallback是Datasource中的数据加载到边界时的回调,为PagedList设置BoundaryCallback用来监听加载本地数据的事件,对应回调方法中为ListStatus设置不同状态

公共ViewModel

open class MyAndroidViewModel(application: Application) : AndroidViewModel(application) {

private fun makePagedListConfig(pageSize: Int = 20): PagedList.Config =

PagedList.Config.Builder().apply {

setPageSize(pageSize)

setInitialLoadSizeHint(pageSize)

setPrefetchDistance(2)

setEnablePlaceholders(false)

}.build()

fun makePagedList(dataSourceFactory: DataSource.Factory,

listStatus: MutableLiveData, pageSize: Int = 20): LiveData> =

LivePagedListBuilder(dataSourceFactory, makePagedListConfig(pageSize))

.setBoundaryCallback(ListBoundaryCallback(listStatus))

.build()

}

ViewModel中设置PagedList.Config,初始化LivePagedListBuilder

自定义PageKeyedDataSource及相关回调

abstract class ListStatusPageKeyedDataSource(private val status: MutableLiveData) : PageKeyedDataSource() {

final override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {

status.postValue(Initialize())

onLoadInitial(params, ListStatusPageKeyedLoadInitialCallback(callback, status))

}

abstract fun onLoadInitial(params: LoadInitialParams, callback: ListStatusPageKeyedLoadInitialCallback)

final override fun loadAfter(params: LoadParams, callback: LoadCallback) {

status.postValue(LoadMoreIn())

onLoadAfter(params, ListStatusPageKeyedLoadCallback(callback, status))

}

abstract fun onLoadAfter(params: LoadParams, callback: ListStatusPageKeyedLoadCallback)

final override fun loadBefore(params: LoadParams, callback: LoadCallback) {

status.postValue(AtFrontLoadMoreIn())

onLoadBefore(params, ListStatusAtFrontPageKeyedLoadCallback(callback, status))

}

abstract fun onLoadBefore(params: LoadParams, callback: ListStatusAtFrontPageKeyedLoadCallback)

}

PageKeyedDataSource是数据源, 将这几种回调替换成我们自定义的回调并抽象出来,改变对应Status状态。

class ListStatusPageKeyedLoadInitialCallback(

private val callback: PageKeyedDataSource.LoadInitialCallback,

private val listStatus: MutableLiveData) : PageKeyedDataSource.LoadInitialCallback() {

override fun onResult(data: MutableList, position: Int, totalCount: Int, previousPageKey: Key?, nextPageKey: Key?) {

callback.onResult(data, position, totalCount, previousPageKey, nextPageKey)

if (!data.isEmpty()) {

listStatus.postValue(InitializeSuccess())

} else {

// 当列表为空时 BoundaryCallback 会回调 Empty 状态因此这里不用处理

}

}

override fun onResult(data: MutableList, previousPageKey: Key?, nextPageKey: Key?) {

callback.onResult(data, previousPageKey, nextPageKey)

if (!data.isEmpty()) {

listStatus.postValue(InitializeSuccess())

} else {

// 当列表为空时 BoundaryCallback 会回调 Empty 状态因此这里不用处理

}

}

fun onError() {

listStatus.postValue(InitializeError())

}

}

class ListStatusPageKeyedLoadCallback(

private val callback: PageKeyedDataSource.LoadCallback,

private val listStatus: MutableLiveData) : PageKeyedDataSource.LoadCallback() {

override fun onResult(data: MutableList, adjacentPageKey: Key?) {

callback.onResult(data, adjacentPageKey)

listStatus.postValue(LoadMoreSuccess())

}

fun onError() {

listStatus.postValue(LoadMoreError())

}

}

class ListStatusAtFrontPageKeyedLoadCallback(

private val callback: PageKeyedDataSource.LoadCallback,

private val listStatus: MutableLiveData) : PageKeyedDataSource.LoadCallback() {

override fun onResult(data: MutableList, adjacentPageKey: Key?) {

callback.onResult(data, adjacentPageKey)

listStatus.postValue(AtFrontLoadMoreSuccess())

}

fun onError() {

listStatus.postValue(AtFrontLoadMoreError())

}

}

这几个回调分别对应初始化、向前加载、向后加载

为请求创建DataSource

class BourseListDataSource(status: MutableLiveData) : ListStatusPageKeyedDataSource(status) {

override fun onLoadInitial(params: LoadInitialParams, callback: ListStatusPageKeyedLoadInitialCallback) {

RetrofitClient.getInstance().getAPI().getBourseList(0, params.requestedLoadSize).enqueue(object : Callback>> {

override fun onResponse(call: Call>>?, response: Response>>) {

val httpResponse = HttpResponseProcess.responseProcess(response)

callback.onResult(httpResponse.data?.list

?: emptyList().toMutableList(), null, 1)

}

override fun onFailure(call: Call>>?, throwable: Throwable) {

val errorResponse = HttpResponseProcess.responseProcess>(throwable)

callback.onError()

}

})

}

override fun onLoadAfter(params: LoadParams, callback: ListStatusPageKeyedLoadCallback) {

RetrofitClient.getInstance().getAPI().getBourseList(params.key * params.requestedLoadSize, params.requestedLoadSize).enqueue(object : Callback>> {

override fun onResponse(call: Call>>?, response: Response>>) {

val httpResponse = HttpResponseProcess.responseProcess(response)

callback.onResult(httpResponse.data?.list

?: emptyList().toMutableList(), params.key + 1)

}

override fun onFailure(call: Call>>?, throwable: Throwable) {

val errorResponse = HttpResponseProcess.responseProcess>(throwable)

callback.onError()

}

})

}

override fun onLoadBefore(params: LoadParams, callback: ListStatusAtFrontPageKeyedLoadCallback) {

}

class Factory(private val status: MutableLiveData) : DataSource.Factory() {

override fun create(): DataSource= BourseListDataSource(status)

}

}

@FormUrlEncoded

@POST("market/getBourseList")

fun getBourseList(@Field("start") start: Int, @Field("size") size: Int): Call>>

接口的DataSource中各回调直接通过Retrofit联网请求数据(没有数据持久化需求),getBourseList api需传的参数为起始位置start与数量size, params.requestedLoadSize就是前面PagedList.Config中所配置PageSize

onLoadInitial中传的start为0

onLoadAfter 中传的start为params.key * params.requestedLoadSize(页码乘每页数量,如果api分页需要的是页码,直接传params.key就可以)

onLoadBefore跟onLoadAfter一样,不需要向上加载的话就置空

请求成功后将数据及下一页页码传给callback的onResult,请求失败直接调用callback的onError

为页面创建ViewModel

class BourseListViewModel(application: Application) : MyAndroidViewModel(application) {

val listStatus = MutableLiveData()

val list = makePagedList(BourseListDataSource.Factory(listStatus), listStatus)

}

ok,就这些...

页面扩展公共方法

fun Fragment.bindList(adapter: AssemblyPagedListAdapter, hint: HintView?, listStatus: MutableLiveData, list: LiveData>, emptyFragment: Fragment? = null,

isFastHidden: Boolean = false, onClickListener: View.OnClickListener? = null) {

list.observe(this, Observer { adapter.submitList(it) })

listStatus.observe(this, Observer {

when (it) {

null -> adapter.loadMoreFinished(false)

is Initialize -> hint?.loading()?.show(childFragmentManager)

is InitializeSuccess -> if (isFastHidden) hint?.fastHidden() else hint?.hidden()

is InitializeError -> hint?.error(onClickListener)?.show(childFragmentManager)

is End -> adapter.loadMoreFinished(true)

is Empty -> if (emptyFragment != null) hint?.empty(emptyFragment)?.show(childFragmentManager) else hint?.empty()?.show(childFragmentManager)

}

})

}

这个仅供参考,AssemblyPagedListAdapter和HintView都是自定义的,大家知道什么意思就行...

页面

class MyListFragment : BaseFragment(){

private val viewModel by bindViewModel(BourseListViewModel::class)

private val adapter = AssemblyPagedListAdapter().apply {

addItemFactory(BourseItem.Factory())

addMoreItem(LoadMoreItem.Factory())

}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

return inflater.inflate(R.layout.fm_list, container, false)

}

override fun initViews(view: View, savedInstanceState: Bundle?) {

recyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)

recyclerView.adapter = adapter

}

override fun loadData() {

bindList(adapter, hintView, viewModel.listStatus, viewModel.list as LiveData>)

}

}

收工,可以看到页面其实只需要初始化View与ViewModel,然后直接bindList就可以了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值