Endless RecyclerView with ProgressBar for pagination
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.
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_movie_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:paddingBottom="50dp"
android:clipToPadding="false"
android:background="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</android.support.v7.widget.RecyclerView>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Here android:paddingBottom="50dp" and android:clipToPadding="false" are very important.
2. Get a reference to the progress bar.
progressBar = findViewById(R.id.progressBar);
3. Define methods to show and hide progress bar.
void showProgressView() {
progressBar.setVisibility(View.VISIBLE);
}
void hideProgressView() {
progressBar.setVisibility(View.INVISIBLE);
}
There is another way to do this.
First your adapter's getItemCount returns
listItems.size() + 1
return
VIEW_TYPE_LOADING
ingetItemViewType()
forposition >= listItems.size()
. This way the loader will only be shown at the end of the recycler view list. The only problem with this solution is even after reaching the last page, the loader will be shown, so in order to fix that you store thex-pagination-total-count
in the adapter, andthen you change the condition to return view type to
(position >= listItem.size())&&(listItem.size <= xPaginationTotalCount)
.
I just came up with this idea now what do you think?
I implemented this on my old project, I did it as follows...
I've created an interface
as the guys of your examples did
public interface LoadMoreItems {
void LoadItems();
}
Then I add added an addOnScrollListener()
on my Adapter
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
//End of the items
if (onLoadMoreListener != null) {
onLoadMoreListener.LoadItems();
}
loading = true;
}
}
});
The onCreateViewHolder()
is where I put the ProgressBar
or not.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, parent, false);
vh = new StudentViewHolder(v);
} else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progressbar_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
On my MainActivity
that is where I put the LoadItems()
to add the others items is :
mAdapter.setOnLoadMoreListener(new LoadMoreItems() {
@Override
public void LoadItems() {
DataItemsList.add(null);
mAdapter.notifyItemInserted(DataItemsList.size() - 1);
handler.postDelayed(new Runnable() {
@Override
public void run() {
// remove progress item
DataItemsList.remove(DataItemsList.size() - 1);
mAdapter.notifyItemRemoved(DataItemsList.size());
//add items one by one
//When you've added the items call the setLoaded()
mAdapter.setLoaded();
//if you put all of the items at once call
// mAdapter.notifyDataSetChanged();
}
}, 2000); //time 2 seconds
}
});
For more information I just followed this Github repository
(Note: this is using AsyncTask
maybe it's useful as my answer, since I did it manually not with data from API
but it should work as well) also this post was helpful to me endless-recyclerview-with-progress-bar
Also 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 to RecyclerView
he's not deleting any item he's just putting Visible/Invisible
the ProgressBar
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 my workaround without adding a fake item (in Kotlin but simple):
in your adapter add:
private var isLoading = false
private val VIEWTYPE_FORECAST = 1
private val VIEWTYPE_PROGRESS = 2
override fun getItemCount(): Int {
if (isLoading)
return items.size + 1
else
return items.size
}
override fun getItemViewType(position: Int): Int {
if (position == items.size - 1 && isLoading)
return VIEWTYPE_PROGRESS
else
return VIEWTYPE_FORECAST
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
if (viewType == VIEWTYPE_FORECAST)
return ForecastHolder(LayoutInflater.from(context).inflate(R.layout.item_forecast, parent, false))
else
return ProgressHolder(LayoutInflater.from(context).inflate(R.layout.item_progress, parent, false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder?, position: Int) {
if (holder is ForecastHolder) {
//init your item
}
}
public fun showProgress() {
isLoading = true
}
public fun hideProgress() {
isLoading = false
}
now you can easily call showProgress()
before API call. and hideProgress()
after API call was done.