Android Paging组件的作用

Android官方的分页加载组件,可以RecyclerView上实现在数据分页加载,无限滚动的效果。官方例子:https://github.com/googlesamples/android-architecture-components/tree/master/PagingWithNetworkSample

需要添加的依赖

implementation "com.android.support:appcompat-v7:28.0.0"implementation "com.android.support:recyclerview-v7:28.0.0"implementation "com.android.support:swiperefreshlayout:28.0.0"implementation "android.arch.paging:runtime:1.0.1"implementation "android.arch.lifecycle:extensions:1.1.1"//使用Androidx的依赖//implementation "androidx.appcompat:appcompat:1.0.2"//implementation "androidx.recyclerview:recyclerview:1.0.0"//implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"//implementation "androidx.paging:paging-runtime:2.0.0"//implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

主要用到这几个类

PagedListAdapter:RecyclerView使用的适配器

DataSource:数据源,常用的子类ItemKeyedDataSource、PageKeyedDataSource

LivePagedListBuilder:实现对象需要传入数据源工厂和请求的页面长度

ItemKeyedDataSource

一般使用id或位置标识加载数据

class DataSourceByItem: ItemKeyedDataSource() {    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {        //初始化加载数据        params.requestedInitialKey//初始化key        params.requestedLoadSize//请求数据的长度        callback.onResult(ArrayList())//回调结果    }    override fun loadAfter(params: LoadParams, callback: LoadCallback) {        //获取下一页的数据        params.key//通过getKey()获取RecyclerView最后一个key        params.requestedLoadSize//请求数据的长度        callback.onResult(ArrayList())//回调结果    }    override fun loadBefore(params: LoadParams, callback: LoadCallback) {        //获取上一页的数据        params.key//通过getKey()获取RecyclerView第一个key        params.requestedLoadSize//请求数据的长度        callback.onResult(ArrayList())//回调结果    }    override fun getKey(item: DataBean): Int {        //获取id(位置标识)        return item.id    }}

PageKeyedDataSource

一般使用页码加载

class DataSourceByPage : PageKeyedDataSource() {    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {        //初始化加载数据        params.requestedLoadSize//请求数据的长度        callback.onResult(ArrayList(),/*上一页的页码*/0,/*下一页的页面*/2)//回调结果    }    override fun loadAfter(params: LoadParams, callback: LoadCallback) {        //获取下一页的数据        params.key//需要请求页面的页码        params.requestedLoadSize//请求数据的长度        callback.onResult(ArrayList(),/*下一页的页码*/params.key + 1)//回调结果    }    override fun loadBefore(params: LoadParams, callback: LoadCallback) {        //获取上一页的数据        params.key//需要请求页面的页码        params.requestedLoadSize//请求数据的长度        callback.onResult(ArrayList(), /*上一页的页码*/params.key - 1)//回调结果    }}

LivePagedListBuilder

private val dataSourceFactory = object : DataSource.Factory() {    override fun create(): DataSource {        return DataSourceByItem()//初始化创建数据源对象,加载上一页或下一页不会创建    }}private val config = PagedList.Config.Builder()    .setInitialLoadSizeHint(60)//初始化请求数据的长度(就是DataSource.loadInitial方法中的params.requestedLoadSize)    .setPageSize(30)//请求数据的长度(loadAfter和loadBefore方法)    .build()private val livePagedList = LivePagedListBuilder(dataSourceFactory, config).build()

PagedListAdapter

class ListAdapter : PagedListAdapter(comparator) {    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {    }    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {    }    companion object {        private val comparator = object : DiffUtil.ItemCallback() {            override fun areItemsTheSame(oldItem: DataBean, newItem: DataBean): Boolean {                //比较唯一id                return oldItem.id == newItem.id            }            override fun areContentsTheSame(oldItem: DataBean, newItem: DataBean): Boolean {                //比较包含对象                return oldItem == newItem            }        }    }}

Demo

使用SwipeRefreshLayout和RecyclerView实现下拉刷新和分页加载

定义加载状态

enum class LoadingState {    Normal, //    Loading, //加载中    Failed, //出错,重试    NotMore,//没有更多数据    Empty//空视图}

数据实体类 

class DataBean  {    var id = 0    var string = "string"}

Activity布局文件

    

activity 

class MainActivity : AppCompatActivity() {    private val viewModel by lazy { ViewModelProviders.of(this)[ListViewModel::class.java] }    private val adapter = ListAdapter { viewModel.retry() }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        recyclerView.adapter = adapter        viewModel.result.observe(this, Observer {            it?.let { result ->                if (result.isInit) {                    if (result.loadingState == LoadingState.Empty) {                        //初始化加载数据为空,设置recyclerView空视图                        recyclerView.post { adapter.setEmptyView() }                    }                    //更新下拉刷新的状态                    swipeRefreshLayout.post {                        swipeRefreshLayout.isRefreshing = (result.loadingState == LoadingState.Loading)                    }                } else {                    recyclerView.post { adapter.setLoadingState(result.loadingState) }                }            }        })        viewModel.livePagedList.observe(this, Observer {            //初始化时,添加数据到适配器            adapter.submitList(it)        })        swipeRefreshLayout.setOnRefreshListener {            //下拉刷新            viewModel.refresh()        }    }}
class ListAdapter(private val retryCallback: () -> Unit) : PagedListAdapter(comparator) {    private var listState = LoadingState.Normal    private fun isShowLastRow(): Boolean {        //是否显示加载视图        return when (listState) {            LoadingState.Loading, LoadingState.Failed, LoadingState.NotMore -> true            else -> false        }    }    private fun isShowEmptyView(): Boolean {        //是否显示空视图        return super.getItemCount() == 0 && listState == LoadingState.Empty    }    override fun getItemCount(): Int {        return super.getItemCount() +                if (isShowLastRow() || isShowEmptyView()) {                    1                } else {                    0                }    }    override fun getItemViewType(position: Int): Int {        return if (isShowLastRow() && position == itemCount - 1) {            R.layout.list_item_loading        } else if (isShowEmptyView()) {            R.layout.list_item_empty        } else {            R.layout.list_item        }    }    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {        val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)        return when (viewType) {            R.layout.list_item -> ItemViewHolder(view)            R.layout.list_item_loading -> LoadingViewHolder(view, retryCallback) { listState }            else -> object : RecyclerView.ViewHolder(view) {}        }    }    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {        when (getItemViewType(position)) {            R.layout.list_item -> getItem(position)?.let { (holder as ItemViewHolder).updateView(it) }            R.layout.list_item_loading -> (holder as LoadingViewHolder).updateView()        }    }    fun setLoadingState(loadingState: LoadingState) {        //更新分页加载状态        if (this.listState == loadingState)            return        val lastItemCount = itemCount        this.listState = loadingState        if (itemCount - 1 == lastItemCount) {            notifyItemInserted(lastItemCount - 1)        } else if (itemCount + 1 == lastItemCount) {            notifyItemRemoved(lastItemCount - 1)        } else if (itemCount == lastItemCount) {            notifyItemChanged(lastItemCount - 1)        }//        notifyItemChanged(lastItemCount - 1)    }    fun setEmptyView() {        //显示空数据视图        if (this.listState == LoadingState.Empty)            return        this.listState = LoadingState.Empty        notifyDataSetChanged()    }    companion object {        private val comparator = object : DiffUtil.ItemCallback() {            override fun areItemsTheSame(oldItem: DataBean, newItem: DataBean) = (oldItem.id == newItem.id)            override fun areContentsTheSame(oldItem: DataBean, newItem: DataBean) = (oldItem == newItem)        }    }}
class ListViewModel : ViewModel() {    private val pageSize = 30    private val config = PagedList.Config.Builder()        .setInitialLoadSizeHint(pageSize * 2)        .setPageSize(pageSize)        .build()    private val dataSource = MutableLiveData()    val result = switchMap(dataSource) { it.getResultBean() }!!    private val dataSourceFactory = object : DataSource.Factory() {        override fun create(): DataSource {            //初始化创建数据源对象,加载上一页或下一页不会创建            return DataSourceByItem().apply { dataSource.postValue(this) }        }    }    val livePagedList = LivePagedListBuilder(dataSourceFactory, config).build()    fun refresh() {        //刷新页面        dataSource.value?.refresh()    }    fun retry() {        //重试        dataSource.value?.retry()    }}
class DataSourceByItem : ItemKeyedDataSource(), IDataSource {    val result = MutableLiveData()    private var retry: (() -> Unit)? = null    override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback) {        result.postValue(ResultBean(true, LoadingState.Loading))        var isReturn = 0        //异步请求远程数据        RemoteData.getById(0, params.requestedLoadSize) { isSuccess, list ->            if (isSuccess) {                result.postValue(ResultBean(true, if (list.isEmpty()) LoadingState.Empty else LoadingState.Normal))                callback.onResult(list)            } else {                result.postValue(ResultBean(true, LoadingState.Failed))                retry = {                    loadInitial(params, callback)                }            }            isReturn = 1        }        //等待请求完成        while (isReturn == 0) {            Thread.sleep(100)        }    }    override fun loadAfter(params: LoadParams, callback: LoadCallback) {        result.postValue(ResultBean(false, LoadingState.Loading))        RemoteData.getById(params.key, params.requestedLoadSize) { isSuccess, list ->            if (isSuccess) {                result.postValue(ResultBean(false, if (list.isEmpty()) LoadingState.NotMore else LoadingState.Normal))                callback.onResult(list)            } else {                result.postValue(ResultBean(false, LoadingState.Failed))                retry = {                    loadAfter(params, callback)                }            }        }    }    override fun loadBefore(params: LoadParams, callback: LoadCallback) {    }    override fun getKey(item: DataBean): Int {        return item.id    }    override fun retry() {        retry?.let {            //重试请求            retry = null            it()        }    }    override fun refresh() {        //初始化刷新        invalidate()    }    override fun getResultBean(): MutableLiveData {        return result    }}

模拟请求远程数据 

object RemoteData {    private var errorCount = 0    fun getById(id: Int, size: Int, callback: (isSuccess: Boolean, list: ArrayList) -> Unit) {        //获取大于id的size条记录,当id大于100模拟一次错误请求,当id大于200返回空记录        Thread {            log("id:$id,size:$size")            val list = arrayListOf()            val start = id + 1            val end = id + size            for (i in start..end) {                list.add(DataBean().apply {                    this.id = i                    this.string = "item$i"                })            }            val isSuccess = if (id < 100) {                errorCount = 1                true            } else {                --errorCount < 0            }            Thread.sleep(2000)            callback(isSuccess, if (id > 200) arrayListOf() else list)        }.start()    }}

完整的项目:https://github.com/dingjianlun/PagingDemo

 

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. Android异步加载图像小结 (含线程池,缓存方法)
  4. Android(安卓)TabHost使用、动态加载内容
  5. Android(安卓)--- BaseAdapter
  6. 在android中policymanager
  7. 【安卓笔记】android客户端与服务端交互的三种方式
  8. Android(安卓)主流图片库Picasso Glide Fresco对比分析
  9. android实践项目一实现简单的验证码和spinner下拉选项效果

随机推荐

  1. android ndk 入门实践
  2. LocationManager Android自动定位使用以
  3. 利用Android的Log 演示一个activity的生
  4. android生命周期
  5. 关于屏幕解锁的实例
  6. android网站汇总
  7. Android(安卓)屏幕滑动事件
  8. Android(安卓)横屏时禁止输入法全屏
  9. Android(安卓)数据绑定(Data Binding)详
  10. Android(安卓)判断是否连接网络