Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An example of how-to implement an infinite scrolling adapter for a RecyclerView, with a ProgressBar footer. Blog post can be found here: http://msobhy.me/2015/09/05/infinite_scrolling_recyclerview/
package net.sarmady.contactcarswithtabs.adapters;
import android.support.annotation.NonNull;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import net.sarmady.contactcarswithtabs.R;
import net.sarmady.contactcarswithtabs.adapters.listener.OnLoadMoreListener;
import java.util.List;
import butterknife.ButterKnife;
import butterknife.InjectView;
/**
* Created by mSobhy on 7/22/15.
*/
public abstract class AbstractRecyclerViewFooterAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int VISIBLE_THRESHOLD = 5;
private final int ITEM_VIEW_TYPE_BASIC = 0;
private final int ITEM_VIEW_TYPE_FOOTER = 1;
private List<T> dataSet;
private int firstVisibleItem, visibleItemCount, totalItemCount, previousTotal = 0;
private boolean loading = true;
public AbstractRecyclerViewFooterAdapter(RecyclerView recyclerView, List<T> dataSet, final OnLoadMoreListener onLoadMoreListener) {
this.dataSet = dataSet;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
totalItemCount = linearLayoutManager.getItemCount();
visibleItemCount = linearLayoutManager.getChildCount();
firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount)
<= (firstVisibleItem + VISIBLE_THRESHOLD)) {
// End has been reached
addItem(null);
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
public int getFirstVisibleItem() {
return firstVisibleItem;
}
public void resetItems(@NonNull List<T> newDataSet) {
loading = true;
firstVisibleItem = 0;
visibleItemCount = 0;
totalItemCount = 0;
previousTotal = 0;
dataSet.clear();
addItems(newDataSet);
}
public void addItems(@NonNull List<T> newDataSetItems) {
dataSet.addAll(newDataSetItems);
notifyDataSetChanged();
}
public void addItem(T item) {
if (!dataSet.contains(item)) {
dataSet.add(item);
notifyItemInserted(dataSet.size() - 1);
}
}
public void removeItem(T item) {
int indexOfItem = dataSet.indexOf(item);
if (indexOfItem != -1) {
this.dataSet.remove(indexOfItem);
notifyItemRemoved(indexOfItem);
}
}
public T getItem(int index) {
if (dataSet != null && dataSet.get(index) != null) {
return dataSet.get(index);
} else {
throw new IllegalArgumentException("Item with index " + index + " doesn't exist, dataSet is " + dataSet);
}
}
public List<T> getDataSet() {
return dataSet;
}
@Override
public int getItemViewType(int position) {
return dataSet.get(position) != null ? ITEM_VIEW_TYPE_BASIC : ITEM_VIEW_TYPE_FOOTER;
}
@Override
public int getItemCount() {
return dataSet.size();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_VIEW_TYPE_BASIC) {
return onCreateBasicItemViewHolder(parent, viewType);
} else if (viewType == ITEM_VIEW_TYPE_FOOTER) {
return onCreateFooterViewHolder(parent, viewType);
} else {
throw new IllegalStateException("Invalid type, this type ot items " + viewType + " can't be handled");
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder genericHolder, int position) {
if (getItemViewType(position) == ITEM_VIEW_TYPE_BASIC) {
onBindBasicItemView(genericHolder, position);
} else {
onBindFooterView(genericHolder, position);
}
}
public abstract RecyclerView.ViewHolder onCreateBasicItemViewHolder(ViewGroup parent, int viewType);
public abstract void onBindBasicItemView(RecyclerView.ViewHolder genericHolder, int position);
public RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType) {
//noinspection ConstantConditions
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.progress_bar, parent, false);
return new ProgressViewHolder(v);
}
public void onBindFooterView(RecyclerView.ViewHolder genericHolder, int position) {
((ProgressViewHolder) genericHolder).progressBar.setIndeterminate(true);
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.progressBar)
public ProgressBar progressBar;
public ProgressViewHolder(View v) {
super(v);
ButterKnife.inject(this, v);
}
}
}
public class MyActivity extends ActionBarActivity {
@InjectView(R.id.myRecyclerView)
RecyclerView mRecyclerView;
RecyclerViewFooterAdapterImpl mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutResource());
mRecyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new RecyclerViewFooterAdapterImpl(mRecyclerView, vehicleModelEngines, new OnLoadMoreListener() {
@Override
public void onLoadMore() {
callEndpointForPage(vehicleMakeModel.getId(), new Runnable() {
@Override
public void run() {
mAdapter.removeItem(null); // don't forget to remove the progress bar representative value
updateAdapter(false);
}
});
}
}, this, currentSortType);
mRecyclerView.setAdapter(mAdapter);
}
private void updateAdapter(boolean shouldGoUp) {
List<MyModelType> myDataSet = new ArrayList<>(MyModelType);
mAdapter.resetItems(myDataSet, currentSortType);
if (shouldGoUp) {
if (mAdapter.getFirstVisibleItem() <= 50) {
mRecyclerView.smoothScrollToPosition(0);
} else {
mRecyclerView.scrollToPosition(0);
}
}
}
}
<ProgressBar
android:id="@+id/progressBar"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="@dimen/space_medium"/>
package net.sarmady.contactcarswithtabs.adapters;
import android.app.Activity;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import net.sarmady.contactcarswithtabs.R;
import net.sarmady.contactcarswithtabs.adapters.listener.OnLoadMoreListener;
import net.sarmady.contactcarswithtabs.adapters.view.holders.UsedVehicleModelEngineViewHolder;
import net.sarmady.contactcarswithtabs.entities.UsedVehiclesSortType;
import net.sarmady.contactcarswithtabs.rest.models.CarImage;
import net.sarmady.contactcarswithtabs.rest.models.UsedVehicleEngine;
import net.sarmady.contactcarswithtabs.ui.listeners.VehicleModelEngineClickListener;
import net.sarmady.contactcarswithtabs.utils.DateUtil;
import net.sarmady.contactcarswithtabs.utils.NumberFormatter;
import java.util.List;
/**
* Created by mSobhy on 7/22/15.
*/
public class RecyclerViewFooterAdapterImpl extends AbstractRecyclerViewFooterAdapter<MyModelType> {
private Activity activity;
private SortType currentSortType;
public RecyclerViewFooterAdapterImpl(RecyclerView recyclerView, List<MyModelType> dataset, OnLoadMoreListener onLoadMoreListener, Activity activity, SortType currentSortType) {
super(recyclerView, usedVehicleEngines, onLoadMoreListener);
this.activity = activity;
this.currentSortType = currentSortType;
}
// An wrapper version of resetItems to allow for manipulating Impl adapter data before reseting dataSet
public void resetItems(@NonNull List<MyModelType> newDataSet, SortType sortType) {
currentSortType = sortType;
resetItems(newDataSet);
}
@Override
public RecyclerView.ViewHolder onCreateBasicItemViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_normal_item_custom_layout, parent, false);
return new MyOwnHolder(v);
}
@Override
public void onBindBasicItemView(RecyclerView.ViewHolder genericHolder, int position) {
final MyOwnHolder holder = (MyOwnHolder) genericHolder;
final MyModelType currentlySelectedItem = getItem(position);
// DO YOU BINDING MAGIC HERE
}
}
@longph92

This comment has been minimized.

Copy link

longph92 commented Apr 4, 2016

totalItemCount = linearLayoutManager.getItemCount();
This line always returned is 0. Why?? Help me please!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.