美文网首页
Android 关于 Paging3 的简单使用

Android 关于 Paging3 的简单使用

作者: 雁过留声_泪落无痕 | 来源:发表于2022-04-21 17:45 被阅读0次
class TestPagingActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val recyclerView = RecyclerView(this)
        recyclerView.layoutManager = LinearLayoutManager(this)

        val adapter = MyAdapter()
        initData(adapter)

        recyclerView.adapter =
            adapter.withLoadStateFooter(object : LoadStateAdapter<MyViewHolder>() {
                override fun displayLoadStateAsItem(loadState: LoadState): Boolean {
                    // also dispatch LoadState.NotLoading
                    return true
                }

                override fun onBindViewHolder(holder: MyViewHolder, loadState: LoadState) {
                    holder.itemView.setOnClickListener(null)
                    when (loadState) {
                        LoadState.Loading -> {
                            (holder.itemView as TextView).text = "Loading..."
                        }
                        is LoadState.Error -> {
                            (holder.itemView as TextView).text = "Load failed. Try again."
                            holder.itemView.setOnClickListener {
                                adapter.retry()
                            }
                        }
                        is LoadState.NotLoading -> {
                            if (loadState.endOfPaginationReached && adapter.itemCount > 0) {
                                (holder.itemView as TextView).text = "All Loaded"
                            }
                        }
                    }
                }

                override fun onCreateViewHolder(
                    parent: ViewGroup,
                    loadState: LoadState
                ): MyViewHolder {
                    val view = TextView(parent.context)
                    view.gravity = Gravity.CENTER
                    view.layoutParams = ViewGroup.LayoutParams(
                        ViewGroup.LayoutParams.MATCH_PARENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    )
                    return MyViewHolder(view)
                }
            })

        val helpWidget = MyHelpWidget(this).apply { setAdapter(adapter) }
        setContentView(FrameLayout(this).apply {
            addView(recyclerView)
            addView(helpWidget)
        })
    }

    private fun initData(adapter: MyAdapter) {
        lifecycleScope.launch {
            loadData().collectLatest {
                adapter.submitData(it)
            }
        }
    }

    private fun loadData(): Flow<PagingData<MyItem>> {
        return Pager(
            config = PagingConfig(enablePlaceholders = false, pageSize = 5),
            pagingSourceFactory = { MyPagingSource() }
        ).flow
    }

    class MyHelpWidget(context: Context) : FrameLayout(context) {
        private val loading = ProgressBar(context)
        private val empty = TextView(context)
        private val error = Button(context)

        init {
            empty.text = "no items."
            error.text = "error! try again."

            addView(loading)
            addView(empty)
            addView(error)
            hide()
        }

        fun <T : Any, VH : RecyclerView.ViewHolder> setAdapter(adapter: PagingDataAdapter<T, VH>) {
            error.setOnClickListener {
                adapter.refresh()
            }

            adapter.addLoadStateListener {
                when (it.refresh) {
                    is LoadState.Error -> {
                        if (adapter.itemCount == 0) {
                            // show error page
                            error()
                        } else {
                            // do not dismiss old items, just show a toast.
                            hide()
                            ToastUtils.showShort("refresh failed.")
                        }
                    }
                    is LoadState.NotLoading -> {
                        if (adapter.itemCount == 0) {
                            // show empty page
                            empty()
                        } else {
                            // show list page
                            hide()
                        }
                    }
                    else -> {
                        // show loading page
                        loading()
                    }
                }
            }
        }

        fun hide() {
            loading.visibility = View.GONE
            empty.visibility = View.GONE
            error.visibility = View.GONE
        }

        fun error() {
            hide()
            error.visibility = View.VISIBLE
        }

        fun empty() {
            hide()
            empty.visibility = View.VISIBLE
        }

        fun loading() {
            hide()
            loading.visibility = View.VISIBLE
        }
    }

    class MyPagingSource : PagingSource<Int, MyItem>() {
        // 如果是最常用的下拉刷新,这里直接返回 null 就可以,表示从头开始加载;
        // 否则如果需要从中间开始加载的话,就按如下方法即可。
        override fun getRefreshKey(state: PagingState<Int, MyItem>): Int? {
            Log.d("xiaobo", "anchorPosition: ${state.anchorPosition}")
            return state.anchorPosition?.let { anchorPosition ->
                val anchorPage = state.closestPageToPosition(anchorPosition)
                anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
            }
        }

        override suspend fun load(params: LoadParams<Int>): LoadResult<Int, MyItem> {
            // main
            // Log.d("xiaobo", "thread: ${Thread.currentThread().name}")

            return try {
                val page = params.key ?: 1
                val list = mutableListOf<MyItem>()
                val result = LoadResult.Page(
                    data = list,
                    prevKey = if (page == 1) null else page - 1,
                    nextKey = if (page == 5) null else page + 1
                )

                withContext(Dispatchers.Default) {
                    delay(800L)
                    if (Random().nextInt(4) == 0) {
                        Log.d("xiaobo", "page: $page, loadSize: ${params.loadSize}")
                        throw RuntimeException("test load failed.")
                    }

                    for (i in 0 until 5) {
                        list.add(MyItem((page - 1) * 5 + i))
                        delay(200L)
                    }
                }
                Log.d("xiaobo", "page: $page, loadSize: ${params.loadSize}, result: $result")

                result
            } catch (e: Exception) {
                LoadResult.Error(e)
            }
        }
    }

    data class MyItem(val num: Int)

    class MyAdapter : PagingDataAdapter<MyItem, MyViewHolder>(MyDiffCallback()) {
        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            getItem(position)?.let {
                (holder.itemView as Button).text = "${it.num} - click to refresh"
            }

            holder.itemView.setOnClickListener {
                // trigger getRefreshKey() be called.
                refresh()
            }
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            val button = Button(parent.context)
            button.isAllCaps = false
            button.layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
            )
            return MyViewHolder(button)
        }
    }

    class MyViewHolder(itemView: View) : BaseViewHolder(itemView)

    class MyDiffCallback : DiffUtil.ItemCallback<MyItem>() {
        override fun areItemsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
            return oldItem.num == newItem.num
        }

        override fun areContentsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
            return oldItem == newItem
        }
    }

}

相关文章

网友评论

      本文标题:Android 关于 Paging3 的简单使用

      本文链接:https://www.haomeiwen.com/subject/thqwertx.html