【问题标题】:Paging 3 initial loading not shown第 3 页初始加载未显示
【发布时间】:2020-12-04 16:40:07
【问题描述】:

我正在处理第 3 页,除了初始加载状态外,一切正常。我正在添加withLoadStateFooter,但它在第一次调用时从不显示加载状态

这是我的实现

负载状态适配器

class LoadStateAdapter (
    private val retry: () -> Unit
): LoadStateAdapter<LoadStateViewHolder>() {

    override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
        holder.bindTo(loadState)
    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        loadState: LoadState
    ): LoadStateViewHolder {
        return LoadStateViewHolder.create(parent, retry)
    }
}

加载状态视图支架

class LoadStateViewHolder(
    view : View,
    private val retryCallback: () -> Unit
) : RecyclerView.ViewHolder(view) {

    private val progressBar = view.findViewById<ProgressBar>(R.id.progress_bar)
    private val errorMsg = view.findViewById<TextView>(R.id.error_msg)
    private val btnRetry = view.findViewById<Button>(R.id.retry_button)
        .also {
            it.setOnClickListener { retryCallback() }
        }
    private var loadState : LoadState? = null

    companion object {
        fun create(parent: ViewGroup, retryCallback: () -> Unit): LoadStateViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.network_state_item, parent, false)
            return LoadStateViewHolder(
                view,
                retryCallback
            )
        }
    }


    fun bindTo(loadState: LoadState) {
        this.loadState = loadState

        btnRetry.isVisible = loadState !is LoadState.Loading
        errorMsg.isVisible = loadState !is LoadState.Loading
        progressBar.isVisible = loadState is LoadState.Loading

        if (loadState is LoadState.Error){
            errorMsg.text = loadState.error.localizedMessage
        }
    }
}

寻呼源

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Model> {

    try {
        // Load page 1 if undefined.
        val currentPage = params.key ?: 0
        val offset = currentPage * 50

        val requestParams = hashMapOf<String, Any>()
        requestParams.put("limit", 50)
        requestParams.put("offset", offset)

        val response =  repository.getList(requestParams)
        val isFinish = response.paging != null && response.paging!!.next == null

        return LoadResult.Page(
            data = response.data ?: mutableListOf(),
            prevKey = null, // Only paging forward.
            nextKey = if (isFinish) null else currentPage + 1
        )
    } catch (e: Exception) {
        // Handle errors in this block
        return LoadResult.Error(e)
    }
}

查看模型

val listPagingFlow = Pager(PagingConfig(pageSize = 50)) {
    MyPagingSource(repository)
}.flow.cachedIn(viewModelScope)

活动

    val pagingAdapter = MyPagingAdapter()
    list.apply {
        setHasFixedSize(true)
        adapter = pagingAdapter.withLoadStateFooter(
            footer = LoadStateAdapter { pagingAdapter.retry() }
        )
    }

    lifecycleScope.launch(Dispatchers.IO) {
        viewModel.listPagingFlow.collectLatest { pagingData ->
            pagingAdapter.submitData(pagingData)
        }
    }

MyPagingAdapter 很简单 PagingDataAdapter

简而言之;加载状态工作正常,但在第一次请求时没有显示。有人可以帮忙吗?

当前版本3.0.0-alpha04

【问题讨论】:

    标签: android paging android-paging android-paging-library


    【解决方案1】:

    withLoadStateFooter 返回一个ConcatAdapter,它将原始PagingDataAdapter 的结果与一个侦听CombinedLoadState.append 事件的LoadStateAdapter 连接起来。因此,它不会在初始加载期间返回项目 (loadType == REFRESH),并且它是这样设计的,因为在任何项目加载之前显示“页脚”实际上没有任何意义。

    但是,要实现您想要的,您可以简单地创建自己的 ConcatAdapter,它非常接近地反映了 .withLoadStateFooter 的实现:

    val originalAdapter = MyPagingDataAdapter(...)
    val footerLoadStateAdapter = MyFooterLoadStateAdapter(...)
    
    addLoadStateListener { loadStates ->
        // You need to implement some logic here to update LoadStateAdapter.loadState
        // based on however you want to prioritize between REFRESH / APPEND load states.
        // Something really basic might be:
        // footerLoadStateAdapter.loadState = when {
        //     loadStates.refresh is NotLoading -> loadStates.append
        //     else -> loadStates.refresh
        // }
        footerLoadStateAdapter.loadState = ...
    }
    return ConcatAdapter(originalAdapter, footerLoadStateAdapter)
    

    【讨论】:

    • 我不明白答案。你能给我一些参考资料或链接吗?
    • 我也没有,我遇到了类似的问题,但无法理解正确的解决方案,有什么帮助吗?
    • 如何澄清上面的示例代码? ConcatAdapter 将来自多个适配器的数据合并为一个,让您可以在 RecyclerView 中显示来自多个适配器的数据。 LoadStateAdapter 是 Paging 库中的一个内置项,它根据 displayLoadStateAsItem 返回的内容显示 0 或 1 个项目,您可以通过公共可变属性设置状态。见developer.android.com/reference/kotlin/androidx/paging/…
    • 此示例展示了如何自定义 LoadStateAdapter 的默认行为的示例,因此您可以选择何时切换到另一个 LoadState。有些人可能会对来自不同 LoadType 的 LoadState 的组合感兴趣,具体取决于他们的用例。
    【解决方案2】:

    使用页脚LoadStateAdapter 显示初始加载会导致问题。如 cmets 中所述,列表将自动滚动到底部。

    解决这个问题的方法是使用两个LoadStateAdapters,一个显示初始加载,第二个显示加载更多项目时的加载。

    fun <T : Any, V : RecyclerView.ViewHolder> PagingDataAdapter<T, V>.withLoadStateAdapters(
        header: LoadStateAdapter<*>,
        footer: LoadStateAdapter<*>
    ): ConcatAdapter {
        addLoadStateListener { loadStates ->
            header.loadState = loadStates.refresh
            footer.loadState = loadStates.append
        }
    
        return ConcatAdapter(header, this, footer)
    }
    

    【讨论】:

      【解决方案3】:

      我建议将初始加载和空状态从分页适配器中取出,并这样做:

      adapter.loadStateFlow.collect {
          // to determine if we are done with the loading state, 
          // you should have already  shown your loading view elsewhere when the entering your fragment
          if (it.prepend is LoadState.NotLoading && it.prepend.endOfPaginationReached) {
              loading.visibility = View.GONE
          }
          if (it.append is LoadState.NotLoading && it.append.endOfPaginationReached) {
              emptyState.isVisible = adapter.itemCount < 1
          }
      }
      

      逻辑是

      • 如果追加已完成(it.append 为 LoadState.NotLoading && it.append.endOfPaginationReached == true),并且我们的适配器项目计数为零(adapter.itemCount ),表示没有什么可显示的,所以我们显示空状态
      • 如果 prepend 已经完成(it.prepend 是 LoadState.NotLoading && it.prepend .endOfPaginationReached == true),那么我们已经完成了初始加载视图,我们应该隐藏它

      【讨论】:

        【解决方案4】:

        作为@dlam 的回答,您可以在适配器中使用自定义 ConcatAdapter 而不是 withLoadStateFooter

        fun withMySpecificFooter(
            footer: LoadStateAdapter<*>
        ): ConcatAdapter {
            addLoadStateListener { loadStates ->
                footer.loadState = when (loadStates.refresh) {
                    is LoadState.NotLoading -> loadStates.append
                    else -> loadStates.refresh
                }
            }
            return ConcatAdapter(this, footer)
        }
        

        【讨论】:

        • 它正在工作,但为什么列出从中间显示的项目?即如果我有 10 个项目,recyclerview 显示第 5 个初始项目,必须滚动到顶部才能看到第一个项目。
        • @HadiAhmadi 同样的问题
        • 每当使用 withLoadStateFooter() 时,我都遇到了从中间开始的 RecyclerView 的相同问题。有谁知道如何解决这个问题?提前致谢。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-09-12
        • 1970-01-01
        • 2018-10-15
        • 2018-08-07
        • 1970-01-01
        • 1970-01-01
        • 2017-09-16
        相关资源
        最近更新 更多