I am using a RecyclerView
and fetching objects from an API in batches of ten. For pagination, I use EndlessRecyclerOnScrollListener
.
It's all working properly. Now all that's left is to add a progress spinner at the bottom of the list while the next batch of objects is fetched by the API. Here is a screenshot of the Google Play Store app, showing a ProgressBar
in what is surely a RecyclerView
:
The problem is, neither the RecyclerView
nor the EndlessRecyclerOnScrollListener
have built-in support for showing a ProgressBar
at the bottom while the next batch of objects is being fetched.
I have already seen the following answers:
1. Put an indeterminate ProgressBar
as footer in a RecyclerView
grid.
2. Adding items to Endless Scroll RecyclerView
with ProgressBar
at bottom.
I am not satisfied with those answers (both by the same person). This involves shoehorning a null
object into the data-set midway while the user is scrolling and then taking it out after the next batch is delivered. It looks like a hack that sidesteps the main problem which may or may not work properly. And it causes a bit of jarring and distortion in the list
Using SwipeRefreshLayout
is not a solution here. SwipeRefreshLayout
involves pulling from the top to fetch the newest items, and it does not show a progress view anyway.
Can someone please provide a good solution for this? I am interested in knowing how Google has implemented this for their own apps (the Gmail app has it too). Are there any articles where this is shown in detail? All answers & comments will be appreciated. Thank you.
Some other references:
1. Pagination with RecyclerView
. (Superb overview ...)
2. RecyclerView
header and footer. (More of the same ...)
I implemented this on my old project, I did it as follows...
I've created an
interface
as the guys of your examples didThen I add added an
addOnScrollListener()
on myAdapter
The
onCreateViewHolder()
is where I put theProgressBar
or not.On my
MainActivity
that is where I put theLoadItems()
to add the others items is :For more information I just followed this
Github repository
(Note: this is usingAsyncTask
maybe it's useful as my answer, since I did it manually not with data fromAPI
but it should work as well) also this post was helpful to me endless-recyclerview-with-progress-barAlso I don't know if you named it but I also found this post infinite_scrolling_recyclerview, maybe it could also help to you.
If it's not what you are looking for, let me know and tell me what's wrong with this code and I'll try to modify it as your like.
Hope it helps.
EDIT
Since you don't want to remove an item... I found I guess one guy that removes the
footer
only on this post : diseño-android-endless-recyclerview.This is for
ListView
but I know you can adapt it toRecyclerView
he's not deleting any item he's just puttingVisible/Invisible
theProgressBar
take a look :detecting-end-of-listview
Also take a look to : this question
android-implementing-progressbar-and-loading-for-endless-list-like-android
HERE IS SIMPLER AND CLEANER APPROACH.
Implement Endless Scrolling from this Codepath Guide and then follow the following steps.
1. Add progress bar under the RecyclerView.
Here android:paddingBottom="50dp" and android:clipToPadding="false" are very important.
2. Get a reference to the progress bar.
3. Define methods to show and hide progress bar.
You can use layout_above tag in recycleView like this:
Different approach would be to start the API call inside onBindViewHolder and initialy place into items view some progress indicator. After call is finished, you update the view (hide progress and showing received data). For example with Picasso for image loading, onBindViewHolder method would look like this
As I see it, there are two cases which can appear:
There response means that you received first chunk of total 100 data. My approach would be something like this:
View After getting first chunk of data (e.g. 10) add them into adapter.
RecyclerView.Adapter.getItemCount As long as the current amount of available items is lower than total amount (e.g. available 10; total 100), in getItemCount method you will return items.size() + 1
RecyclerView.Adapter.getItemViewType if total amount of data is greater than amount of available items in adapter and the position = items.size() (i.e. you’ve fictively added item in getItemCount method), as view type you return some progress-indicator. Otherwise you’ll return normal layout type
RecyclerView.Adapter.onCreateViewHolder When you are asked to use progress-indicator view type, all you need to do is to ask your presenter to get additional chunk of items and update the adapter
So basically, this is approach where you don’t have to add/remove items from the list and where you have control over situation when lazy loading will be triggered.
Here is the code example:
Maybe this'll inspire you to make something that will suit your needs
here is my workaround without adding a fake item (in Kotlin but simple):
in your adapter add:
now you can easily call
showProgress()
before API call. andhideProgress()
after API call was done.I like the idea of adding a progress view holder to an adapter, but it tends to lead to some ugly logic manipulation to get what you want. The view holder approach forces you to guard against the additional footer item by fidgeting with the return values of
getItemCount()
,getItemViewType()
,getItemId(position)
and any kind ofgetItem(position)
method that you may want to include.An alternative approach is to manage the
ProgressBar
visibility at theFragment
orActivity
level by showing or hiding theProgressBar
below theRecyclerView
when loading starts and ends respectively. This can be achieved by including theProgressBar
directly in the view layout or by adding it to a customRecyclerView
ViewGroup
class. This solution will generally lead to less maintenance and fewer bugs.UPDATE: My suggestion poses a problem when you scroll the view back up while the content is loading. The
ProgressBar
will stick to the bottom of the view layout. This is probably not the behavior you want. For this reason, adding a progress view holder to your adapter is probably the best, functional solution. Just don't forget to guard your item accessor methods. :)