9

I'm following the Codelab of Paging 3.

The paging is working fine, but trying to add a Footer does not seem to work.

My code is exactly like the Codelab one when it comes to LoadStateAdapter used

class ListLoadStateAdapter(
    private val retry: () -> Unit,
) : LoadStateAdapter<ListLoadStateAdapter.ListLoadStateViewHolder>() {

    override fun onBindViewHolder(holder: ListLoadStateViewHolder, loadState: LoadState) {
        holder.bind(loadState)
    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        loadState: LoadState,
    ) = ListLoadStateViewHolder.create(parent, retry)

    class ListLoadStateViewHolder(
        private val binding: ComponentPagedListFooterBinding,
        retry: () -> Unit,
    ) : RecyclerView.ViewHolder(binding.root) {

        init {
            binding.buttonRetry.setOnClickListener { retry.invoke() }
        }

        fun bind(loadState: LoadState) {
            if (loadState is LoadState.Error) {
                binding.textViewPlaceholderError.text = loadState.error.localizedMessage
            }

            binding.progressBar.isVisible = loadState is LoadState.Loading
            binding.buttonRetry.isVisible = loadState is LoadState.Error
            binding.textViewPlaceholderError.isVisible = loadState is LoadState.Error

//            binding.root.isVisible = loadState is LoadState.Loading || loadState is LoadState.Error
        }

        companion object {
            fun create(parent: ViewGroup, retry: () -> Unit): ListLoadStateViewHolder {
                val binding = ComponentPagedListFooterBinding.inflate(
                    LayoutInflater.from(parent.context),
                    parent,
                    false,
                )

                return ListLoadStateViewHolder(binding, retry)
            }
        }
    }
}

And this is how I add the footer

adapter = this@InvoiceListFragment.adapter.apply {
                withLoadStateFooter(ListLoadStateAdapter { retry() })

                addLoadStateListener {
                    viewModel.handlePagingState(it, this)
                }
            }

handlePagingState is just to follow the state and bind it to the Page state (Loading, Error, Empty, etc). Removing it changed nothing anyways.

The ListLoadStateAdapter.onCreateViewHolder() doesn't even get called, neither the constructor for ListLoadStateViewHolder.

What am I doing wrong? Is there something I missed? Or maybe a bug somewhere?

A-Android UCG
  • 797
  • 1
  • 7
  • 23

4 Answers4

15

My issue was that I was not setting the ConcatAdapter returned by withLoadStateFooter

A-Android UCG
  • 797
  • 1
  • 7
  • 23
  • 1
    where would you have to set it? – mantc_sdr Sep 12 '21 at 21:16
  • 2
    @mantc_sdr After these `yourAdapter.addLoadStateListener{} val contcatAdapter = yourAdapter.withLoadStateFooter(footer = YourLoadStateFooter{rourAdapter.retry()})`, you just do `rourRecyclerView.adapter = concatAdapter` – ravi Dec 08 '21 at 11:07
  • Set the returned `ConcatAdapter` to your `recyclerView` instead of the `PagingDataAdapter`. `recyclerView.setAdapter(concatAdapter);` – Roshana Pitigala Jul 19 '22 at 23:16
2

If anyone is still facing the same issue. You have to apply new adapter to the recycler view.

val adapterWithLoading = adapter.withLoadStateFooter(PagingLoadStateAdapter(adapter::retry))

binding.recycler.apply {
    layoutManager = LinearLayoutManager(context)
    adapter = adapterWithLoading
    addItemDecoration(ItemHorizontalDecorator())
}
Wolf
  • 97
  • 12
0

I faced the save issue. My problem was that I initially try to load items from cache without internet and in that case footer didn't show up. When I load items from api than off internet - footer work normal.

0

Improved from the answer: https://stackoverflow.com/a/67228677/7735068

I have created an extension function for the same,

fun <T : Any, VH : RecyclerView.ViewHolder> PagingDataAdapter<T, VH>.loadFooter(): ConcatAdapter {

    Timber.e("Appending footer")
    return this.withLoadStateFooter(
        footer = FooterAdapter {
            this.retry()
        }
    )
}

So that we can call it directly during recycler view adapter initialization as,

rv.adapter = customPagingAdapter.loadFooter()

Rest of the FooterAdapter Impl. is same as codelabs.

Willey Hute
  • 939
  • 13
  • 18