Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initial placeholder with Paging Architecture Components

I'm using the Paging Architecture Components in my project to load a list from the network (no database usage yet). My DataSource is a PositionalDataSource subclass, and my PagedList.Config looks like this:

PagedList.Config config = new PagedList.Config.Builder()
        .setPageSize(10)
        .setInitialLoadSizeHint(20)
        .setPrefetchDistance(20)
        .setEnablePlaceholders(true)
        .build();

Placeholders are enabled, and I handle null ViewHolders in my PagedListAdapter subclass to display placeholder items differently. The placeholders work fine when loading additional items at the end of the list, but I also want to display a placeholder item before the first few items are loaded (like a regular loading indicator).

Is there a way to do this with the Paging library? I tried to call LoadInitialCallback.onResult() with a position of 1 instead of 0, but this only creates a placeholder item before the first real item, and that placeholder item never disappears.

like image 289
ofalvai Avatar asked Sep 11 '18 12:09

ofalvai


People also ask

How Paging works in android?

The Paging Library lets you load data directly from your backend using keys that the network provides. Your data can be uncountably large. Using the Paging Library, you can load data into pages until there isn't any data remaining. You can observe your data more easily.

How do I use PagedList on Android?

PagedList can present data for an unbounded, infinite scrolling list, or a very large but countable list. Use PagedList. Config to control how many items a PagedList loads, and when. If you use LivePagedListBuilder to get a LiveData <PagedList>, it will initialize PagedLists on a background thread for you.

How can I get data from PagingData?

Display the paged data in your UICreate an instance of your PagingDataAdapter class. Pass the PagingDataAdapter instance to the RecyclerView list that you want to display your paged data. Observe the PagingData stream, and pass each generated value to your adapter's submitData() method.

Which API defines the source of data to be retrieved from a single source in paging?

Repository layer Each PagingSource object defines a source of data and how to retrieve data from that source. A PagingSource object can load data from any single source, including network sources and local databases.


1 Answers

I realize this question is rather old but I answer this, since this is where I ended up from googling the issue . In v3 there are LoadStateAdapter which you can add to Header and Footer for Prepend and Append load states. Initial load will be the refresh state which does not seem to be included but we can simply copy the functionality and extend it to include Refresh:

I simply copied withLoadStateHeaderAndFooter and added a third adapter that could be used for refresh

private fun PagingAdapter.withLoadStateAll(
        header: LoadStateAdapter<*>,
        footer: LoadStateAdapter<*>,
        refresh: LoadStateAdapter<*>
    ): ConcatAdapter {
        addLoadStateListener { loadStates ->
            header.loadState = loadStates.prepend
            footer.loadState = loadStates.append
            refresh.loadState = loadStates.refresh
        }
        return ConcatAdapter(refresh,header, this, footer)
    }

then you can simply register this to your Recycler as per the documentation and it'll show during initial load.

recycler.adapter = adapter.withLoadStateAll(LoadAdapter(),LoadAdapter(),LoadAdapter())

If you need to show more than one item during initial load simply copy LoadStateAdapter and change the number of items from 1 to n:

class InitialLoadAdapter(private val num : Int) : RecyclerView.Adapter<LoadViewHolder>() {

    var loadState: LoadState = LoadState.NotLoading(endOfPaginationReached = false)
        set(loadState) {
            if (field != loadState) {
                val oldItem = displayLoadStateAsItem(field)
                val newItem = displayLoadStateAsItem(loadState)

                if (oldItem && !newItem) {
                    notifyItemRangeRemoved(0,num)
                } else if (newItem && !oldItem) {
                    notifyItemRangeInserted(0,num)
                } else if (oldItem && newItem) {
                    notifyItemRangeChanged(0,num)
                }
                field = loadState
            }
        }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LoadViewHolder {
        return LoadViewHolder(parent)
    }
    override fun onBindViewHolder(holder: LoadViewHolder, position: Int) {}

    override fun getItemViewType(position: Int): Int = getStateViewType(loadState)

    override fun getItemCount(): Int = if (displayLoadStateAsItem(loadState)) num else 0

    private fun getStateViewType(loadState: LoadState): Int = 0

    private fun displayLoadStateAsItem(loadState: LoadState): Boolean {
        return loadState is LoadState.Loading || loadState is LoadState.Error
    }
}
like image 130
Ramin Avatar answered Oct 19 '22 19:10

Ramin