Skip to content

Instantly share code, notes, and snippets.

@mSobhy90
Last active March 11, 2017 04:54
Show Gist options
  • Save mSobhy90/cf7fa98803a0d7716a4a to your computer and use it in GitHub Desktop.
Save mSobhy90/cf7fa98803a0d7716a4a to your computer and use it in GitHub Desktop.
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
}
}
@pnlgcoding
Copy link

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