How to check the list size or for empty list in Paging 3 library
Asked Answered
O

2

10

I've been able to successfully implement the new alpha07 version of Paging 3 library by following the instructions available here: https://developer.android.com/topic/libraries/architecture/paging/v3-paged-data#guava-livedata

However, now I am in need of checking whether the returned list is empty or not in order to show a view or a text to the user, but I am not able to attach any checks in the flow structure of their paging design.

Currently this is how I have my code in Java in my onViewCreated after following their guides:

        MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);

        LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
        Lifecycle lifecycle = lifecycleOwner.getLifecycle();
        Pager<Integer, MyEntity> pager = new Pager<>(new PagingConfig(10), () -> viewModel.getMyPagingSource());
        LiveData<PagingData<MyEntity>> pagingDataLiveData = PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), lifecycle);

        pagingDataLiveData.observe(lifecycleOwner, data -> adapter.submitData(lifecycle, data));

I tried attaching a .filter on my data in the adapter.submitData(lifecycle, data), but it never receives null items despite the list being empty.

How can I check when the data submitted to the adapter is empty in this scenario? I couldn't find any pointers in their documentation.

EDIT: This is the solution I found, posting here because the selected answer is not strictly speaking the solution nor is it in java, but is the one that lead me to it.

I had to attach a LoadStateListener to my adapter, listen for when the LoadType is REFRESH and the LoadState is NotLoading, then check if the adapter.getItemCount is 0.

It's possible a different LoadType is more appropriate for this scenario, but the refresh is working so far for me so I went with that one.

Sample example:

// somewhere when you initialize your adapter
...
myAdapter.addLoadStateListener(this::loadStateListener);
...
private Unit loadStateListener(@Nonnull CombinedLoadStates combinedLoadStates) {

    if (!(combinedLoadStates.getRefresh() instanceof LoadState.NotLoading)) {
        return Unit.INSTANCE; // this is the void equivalent in kotlin
    }

    myView.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.INVISIBLE);

    return Unit.INSTANCE; // this is the void equivalent in kotlin

}

NOTE: We have to return Unit.INSTANCE because that listener is in kotlin and that code is being used in java, returning this is the equivalent of not returning anything in java (void).

Opportuna answered 21/10, 2020 at 18:11 Comment(0)
B
12

The PagingData operators operate on individual items, so they won't get triggered if the page is empty.

Instead you want to check PagingDataAdapter.itemCount after the page has loaded, by observing loadStateFlow.

loadStateFlow.map { it.refresh }
    .distinctUntilChanged()
    .collect {
        if (it is NotLoading) {
            // PagingDataAdapter.itemCount here
        }
    }

Btw, null is a special value in Paging that denotes a placeholder, so you should not give Paging pages containing nulls, this is why the lower bound for the Value generic is non-null Any rather than Any?

Bosco answered 25/10, 2020 at 17:2 Comment(5)
thanks for your input, but I have no idea how to implement this in java, this seems to be pure kotlin and like I mentioned in my question I am using java at the moment. Would it be possible to provide a java equivalent of that example you gave?Opportuna
In Java you would use loadStateListener instead, or you can convert the flow to an rx stream if you're using rxjava. There are also toLiveData helpers available as well if that's your preferred streaming APIBosco
I cannot understand how to correlate the addLoadStateListener in the way you suggested. This Paging 3 is extremely confusing and lacking a ton of documentation which makes this so much more difficult to understand.Opportuna
I think I might have made some progress, would this be correct to check for emptiness in this case? combinedLoadStates.getRefresh() instanceof LoadState.NotLoading && myPagingDataAdapter.getItemCount() == 0 I am trying to do it based on your original answer which points to is NotLoadingOpportuna
Yes, but the listener will trigger when any loadState changes including for append and prepend, so you want to filter for just changes in refresh. Although if you're just setting a view's visiblity to GONE redundantly, it shouldn't be too badBosco
L
4

Unfortunately accepted answer didn't work well for my case. So instead of checking adapter's itemCount I throw custom exception (lets call it EmptyListException) when api returns empty list (by design it shouldn't). In my PagingSource.load() method i check if fetched data is empty:

return if (pagedResponse.page.isEmpty()){
    LoadResult.Error(EmptyListException())
} else {
    LoadResult.Page(pagedResponse.page, prevKey, nextKey)
}

Then i process result in the adapter.loadStateFlow.

Librate answered 2/12, 2020 at 16:54 Comment(1)
This approach seemed very useful to me for Jetpack Compose.Silden

© 2022 - 2024 — McMap. All rights reserved.