Skip to content

Instantly share code, notes, and snippets.

@nesquena
Forked from rogerhu/Contact.java
Last active January 15, 2023 13:03
Show Gist options
  • Save nesquena/8a976dd3d6f866518db2cfe7f9cb0db7 to your computer and use it in GitHub Desktop.
Save nesquena/8a976dd3d6f866518db2cfe7f9cb0db7 to your computer and use it in GitHub Desktop.
Endless Scrolling with RecyclerVIew
package codepath.com.recyclerviewfun;
import java.util.ArrayList;
import java.util.List;
public class Contact {
private String mName;
private boolean mOnline;
public Contact(String name, boolean online) {
mName = name;
mOnline = online;
}
public String getName() {
return mName;
}
public boolean isOnline() {
return mOnline;
}
private static int lastContactId = 0;
public static List<Contact> createContactsList(int numContacts, int offset) {
List<Contact> contacts = new ArrayList<Contact>();
for (int i = 1; i <= numContacts; i++) {
contacts.add(new Contact("Person " + ++lastContactId + " offset: " + offset, i <= numContacts / 2));
}
return contacts;
}
}
package codepath.com.recyclerviewfun;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import java.util.List;
// Create the basic adapter extending from RecyclerView.Adapter
// Note that we specify the custom ViewHolder which gives us access to our views
public class ContactsAdapter extends
RecyclerView.Adapter<ContactsAdapter.ViewHolder> {
// Store a member variable for the contacts
private List<Contact> mContacts;
// Pass in the contact array into the constructor
public ContactsAdapter(List<Contact> contacts) {
mContacts = contacts;
}
// Provide a direct reference to each of the views within a data item
// Used to cache the views within the item layout for fast access
public static class ViewHolder extends RecyclerView.ViewHolder {
// Your holder should contain a member variable
// for any view that will be set as you render a row
public TextView nameTextView;
public Button messageButton;
// We also create a constructor that accepts the entire item row
// and does the view lookups to find each subview
public ViewHolder(View itemView) {
// Stores the itemView in a public final member variable that can be used
// to access the context from any ViewHolder instance.
super(itemView);
nameTextView = (TextView) itemView.findViewById(R.id.contact_name);
messageButton = (Button) itemView.findViewById(R.id.message_button);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View contactView = inflater.inflate(R.layout.item_contact, parent, false);
ViewHolder viewHolder = new ViewHolder(contactView);
return viewHolder;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
Contact contact = mContacts.get(position);
TextView textView = viewHolder.nameTextView;
textView.setText(contact.getName());
Button button = viewHolder.messageButton;
if (contact.isOnline()) {
button.setText("Message");
button.setEnabled(true);
}
else {
button.setText("Offline");
button.setEnabled(false);
}
}
@Override
public int getItemCount() {
return mContacts.size();
}
}
public abstract class EndlessRecyclerViewScrollListener extends RecyclerView.OnScrollListener {
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
// The current offset index of data you have loaded
private int currentPage = 0;
// The total number of items in the dataset after the last load
private int previousTotalItemCount = 0;
// True if we are still waiting for the last set of data to load.
private boolean loading = true;
// Sets the starting page index
private int startingPageIndex = 0;
RecyclerView.LayoutManager mLayoutManager;
public EndlessRecyclerViewScrollListener(LinearLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
}
public EndlessRecyclerViewScrollListener(GridLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}
public EndlessRecyclerViewScrollListener(StaggeredGridLayoutManager layoutManager) {
this.mLayoutManager = layoutManager;
visibleThreshold = visibleThreshold * layoutManager.getSpanCount();
}
public int getLastVisibleItem(int[] lastVisibleItemPositions) {
int maxSize = 0;
for (int i = 0; i < lastVisibleItemPositions.length; i++) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i];
}
else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i];
}
}
return maxSize;
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
@Override
public void onScrolled(RecyclerView view, int dx, int dy) {
int lastVisibleItemPosition = 0;
int totalItemCount = mLayoutManager.getItemCount();
if (mLayoutManager instanceof StaggeredGridLayoutManager) {
int[] lastVisibleItemPositions = ((StaggeredGridLayoutManager) mLayoutManager).findLastVisibleItemPositions(null);
// get maximum element within the list
lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions);
} else if (mLayoutManager instanceof LinearLayoutManager) {
lastVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastVisibleItemPosition();
} else if (mLayoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) mLayoutManager).findLastVisibleItemPosition();
}
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex;
this.previousTotalItemCount = totalItemCount;
if (totalItemCount == 0) {
this.loading = true;
}
}
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false;
previousTotalItemCount = totalItemCount;
}
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
// threshold should reflect how many total columns there are too
if (!loading && (lastVisibleItemPosition + visibleThreshold) > totalItemCount) {
currentPage++;
onLoadMore(currentPage, totalItemCount);
loading = true;
}
}
// Defines the process for actually loading more data based on page
public abstract void onLoadMore(int page, int totalItemsCount);
}
package codepath.com.recyclerviewfun;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
RecyclerView rvItems = (RecyclerView) findViewById(R.id.rvContacts);
final List<Contact> allContacts = Contact.createContactsList(10, 0);
final ContactsAdapter adapter = new ContactsAdapter(allContacts);
rvItems.setAdapter(adapter);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
rvItems.setLayoutManager(linearLayoutManager);
rvItems.addOnScrollListener(new EndlessRecyclerViewScrollListener(linearLayoutManager) {
@Override
public void onLoadMore(int page, int totalItemsCount) {
List<Contact> moreContacts = Contact.createContactsList(10, page);
int curSize = adapter.getItemCount();
allContacts.addAll(moreContacts);
adapter.notifyItemRangeInserted(curSize, allContacts.size() - 1);
}
});
}
}
@anggadarkprince
Copy link

This code works fine before when I tried to combine with scrollview to making profile screen which contain some imageview, textview, etc, then add recyclerview below all of those views, the recyclerview set wrap-content of height but the recyclerview scrollevent never fired, how to fixed this thing...thanks..

@gblnovaes
Copy link

very good reference :-)

@Sarosh-Madara
Copy link

Sarosh-Madara commented Oct 13, 2016

hey i have a problem executing.... the method onLoadmore() doesn't get called

mNewsRV = (RecyclerView) view.findViewById(R.id.newsRV);
    mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipeRefreshLayout);
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity(),LinearLayoutManager.VERTICAL,false);
    mNewsRV.addOnScrollListener(new EndlessRecyclerViewScrollListener(linearLayoutManager) {
        @Override
        public void onLoadMore(int page, int totalItemsCount) {

            Log.d(TAG,"load more called");
            Helpers.showTost(getActivity(),"load more called");
            mSwipeRefreshLayout.setRefreshing(true);
            fetchData();
        }
    });

    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            mSwipeRefreshLayout.setRefreshing(true);
            mNewsList.clear();
            mAdapter.notifyDataSetChanged();
            fetchAllData();
        }
    });

@kukalajet
Copy link

@anggadarkprince did you find a solution?

@TrioangleTech
Copy link

TrioangleTech commented Nov 24, 2018

For me lastVisibleItemPosition returns wrongly . it returns total item count -1 . For Example if total item count was 40 then lastVisibleItemPosition returns 39 and so on. So all the pages were called by the first time itself. Any Solution ?

@AnthonyKoueik
Copy link

@TrioangleTech did you fix it ?

@abbasalid
Copy link

@Sarosh-Madara hello did u get solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment