¿Cómo implementar una lista interminable con RecyclerView?

Me gustaría cambiar ListView a RecyclerView . Quiero usar el onScroll de OnScrollListener en RecyclerView para determinar si un usuario se desplazó al final de la lista.

¿Cómo puedo saber si un usuario se desplaza hasta el final de la lista para poder obtener datos nuevos de un servicio REST?

Gracias a @Kushal y así es como lo implementé

 private boolean loading = true; int pastVisiblesItems, visibleItemCount, totalItemCount; mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if(dy > 0) //check for scroll down { visibleItemCount = mLayoutManager.getChildCount(); totalItemCount = mLayoutManager.getItemCount(); pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition(); if (loading) { if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount) { loading = false; Log.v("...", "Last Item Wow !"); //Do pagination.. ie fetch new data } } } } }); 

No te olvides de agregar

 LinearLayoutManager mLayoutManager; mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); 

Haz estas variables.

 private int previousTotal = 0; private boolean loading = true; private int visibleThreshold = 5; int firstVisibleItem, visibleItemCount, totalItemCount; 

Establecer en desplazamiento para la vista de reciclador.

 mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); visibleItemCount = mRecyclerView.getChildCount(); totalItemCount = mLayoutManager.getItemCount(); firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); if (loading) { if (totalItemCount > previousTotal) { loading = false; previousTotal = totalItemCount; } } if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) { // End has been reached Log.i("Yaeye!", "end called"); // Do something loading = true; } } }); 

Nota: asegúrese de estar utilizando LinearLayoutManager como administrador de diseño para RecyclerView .

 LinearLayoutManager mLayoutManager; mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); 

y para una grilla

 GridLayoutManager mLayoutManager; mLayoutManager = new GridLayoutManager(getActivity(), spanCount); mRecyclerView.setLayoutManager(mLayoutManager); 

¡Diviértete con tus rollos sin fin! ^. ^

Actualización: mRecyclerView. setOnScrollListener () está en desuso simplemente reemplace con mRecyclerView.addOnScrollListener() y la advertencia desaparecerá. Puedes leer más de esta pregunta SO .

Dado que Android ahora es compatible oficialmente con Kotlin, aquí hay una actualización para el mismo:

Hacer OnScrollListener

 class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter, val dataList: MutableList) : RecyclerView.OnScrollListener() { var previousTotal = 0 var loading = true val visibleThreshold = 10 var firstVisibleItem = 0 var visibleItemCount = 0 var totalItemCount = 0 override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) visibleItemCount = recyclerView.childCount totalItemCount = layoutManager.itemCount firstVisibleItem = layoutManager.findFirstVisibleItemPosition() if (loading) { if (totalItemCount > previousTotal) { loading = false previousTotal = totalItemCount } } if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) { val initialSize = dataList.size updateDataList(dataList) val updatedSize = dataList.size recyclerView.post { adapter.notifyItemRangeInserted(initialSize, updatedSize) } loading = true } } } 

y agrégalo a tu RecyclerView como este

 recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList)) 

Para un ejemplo de código completo, siéntase libre de referirse a este repository de Github .

Para aquellos que solo desean recibir notificaciones cuando se muestra totalmente el último elemento, puede usar View.canScrollVertically() .

Aquí está mi implementación:

 public abstract class OnVerticalScrollListener extends RecyclerView.OnScrollListener { @Override public final void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (!recyclerView.canScrollVertically(-1)) { onScrolledToTop(); } else if (!recyclerView.canScrollVertically(1)) { onScrolledToBottom(); } else if (dy < 0) { onScrolledUp(); } else if (dy > 0) { onScrolledDown(); } } public void onScrolledUp() {} public void onScrolledDown() {} public void onScrolledToTop() {} public void onScrolledToBottom() {} } 

Nota: Puede usar recyclerView.getLayoutManager().canScrollVertically() si desea admitir API <14.

Aquí hay otro enfoque. Funcionará con cualquier administrador de diseño.

  1. Hacer clase de adaptador abstracto
  2. A continuación, cree un método abstracto en la clase de adaptador (por ejemplo, load ())
  3. En onBindViewHolder comprueba la posición si es la última y call load ()
  4. Anule la función load () mientras crea el objeto adaptador en su actividad o fragmento.
  5. En la función de carga sobrecargada, implemente su llamada loadmore

Para obtener información detallada, escribí una publicación de blog y un proyecto de ejemplo. Consíguelo aquí http://sab99r.com/blog/recyclerview-endless-load-more/

MyAdapter.java

 public abstract class MyAdapter extends RecyclerView.Adapter{ @Override public void onBindViewHolder(ViewHolder holder, int position) { //check for last item if ((position >= getItemCount() - 1)) load(); } public abstract void load(); } 

MyActivity.java

 public class MainActivity extends AppCompatActivity { List items; MyAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { ... adapter=new MyAdapter(items){ @Override public void load() { //implement your load more here Item lastItem=items.get(items.size()-1); loadMore(); } }; } } 

Mi respuesta es una versión modificada de Noor. Pasé de un ListView donde tuve EndlessScrollListener (que puede encontrar fácilmente en muchas respuestas en SO) a un RecyclerView así que quería un EndlessRecyclScrollListener para actualizar fácilmente mi oyente pasado.

Así que aquí está el código, espero que ayude:

 public abstract class EndlessScrollRecyclListener extends RecyclerView.OnScrollListener { // The total number of items in the dataset after the last load private int previousTotalItemCount = 0; private boolean loading = true; private int visibleThreshold = 5; int firstVisibleItem, visibleItemCount, totalItemCount; private int startingPageIndex = 0; private int currentPage = 0; @Override public void onScrolled(RecyclerView mRecyclerView, int dx, int dy) { super.onScrolled(mRecyclerView, dx, dy); LinearLayoutManager mLayoutManager = (LinearLayoutManager) mRecyclerView .getLayoutManager(); visibleItemCount = mRecyclerView.getChildCount(); totalItemCount = mLayoutManager.getItemCount(); firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); onScroll(firstVisibleItem, visibleItemCount, totalItemCount); } public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) { // 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; currentPage++; } // 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. if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) { onLoadMore(currentPage + 1, totalItemCount); loading = true; } } // Defines the process for actually loading more data based on page public abstract void onLoadMore(int page, int totalItemsCount); } 

Para mí, es muy simple:

  private boolean mLoading = false; mList.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int totalItem = mLinearLayoutManager.getItemCount(); int lastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition(); if (!mLoading && lastVisibleItem == totalItem - 1) { mLoading = true; // Scrolled to bottom. Do something here. mLoading = false; } } }); 

Tenga cuidado con los trabajos asíncronos: el mLoading se debe cambiar al final de los trabajos asincrónicos. ¡Espero que sea de ayuda!

La mayoría de las respuestas se LinearLayoutManager RecyclerView utiliza un LinearLayoutManager , o GridLayoutManager , o incluso StaggeredGridLayoutManager , o suponiendo que el desplazamiento es vertical o horyzontal, pero nadie ha publicado una respuesta completamente genérica .

Usar el adaptador de ViewHolder claramente no es una buena solución. Un adaptador puede tener más de 1 RecyclerView usándolo. Se “adapta” sus contenidos. Debería ser la Vista de Reciclador (que es la única clase responsable de lo que se muestra actualmente al usuario, y no el adaptador que es el único responsable de proporcionar contenido a RecyclerView ) que debe notificar a su sistema que se necesitan más elementos ( cargar).

Aquí está mi solución, usando nada más que las clases abstractas de RecyclerView (RecycerView.LayoutManager y RecycerView.Adapter):

 /** * Listener to callback when the last item of the adpater is visible to the user. * It should then be the time to load more items. **/ public abstract class LastItemListener extends RecyclerView.OnScrollListener { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); // init RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); RecyclerView.Adapter adapter = recyclerView.getAdapter(); if (layoutManager.getChildCount() > 0) { // Calculations.. int indexOfLastItemViewVisible = layoutManager.getChildCount() -1; View lastItemViewVisible = layoutManager.getChildAt(indexOfLastItemViewVisible); int adapterPosition = layoutManager.getPosition(lastItemViewVisible); boolean isLastItemVisible = (adapterPosition == adapter.getItemCount() -1); // check if (isLastItemVisible) onLastItemVisible(); // callback } } /** * Here you should load more items because user is seeing the last item of the list. * Advice: you should add a bollean value to the class * so that the method {@link #onLastItemVisible()} will be triggered only once * and not every time the user touch the screen ;) **/ public abstract void onLastItemVisible(); } // --- Exemple of use --- myRecyclerView.setOnScrollListener(new LastItemListener() { public void onLastItemVisible() { // start to load more items here. } } 

Aunque la respuesta aceptada funciona perfectamente, la siguiente solución utiliza addOnScrollListener ya que setOnScrollListener está en desuso, y reduce el número de variables, y si las condiciones.

 final LinearLayoutManager layoutManager = new LinearLayoutManager(context); feedsRecyclerView.setLayoutManager(layoutManager); feedsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (dy > 0) { if ((layoutManager.getChildCount() + layoutManager.findFirstVisibleItemPosition()) >= layoutManager.getItemCount()) { Log.d("TAG", "End of list"); //loadMore(); } } } }); 

Aunque hay tantas respuestas a la pregunta, me gustaría compartir nuestra experiencia de crear la vista de lista infinita. Recientemente hemos implementado Carrusel LayoutManager personalizado que puede funcionar en el ciclo al desplazar la lista infinitamente y hasta cierto punto. Aquí hay una descripción detallada de GitHub .

Le sugiero que eche un vistazo a este artículo con recomendaciones breves pero valiosas sobre cómo crear LayoutManagers personalizados: http://cases.azoft.com/create-custom-layoutmanager-android/

Mi forma de detectar el evento de carga no es detectar el desplazamiento, sino escuchar si se adjuntó la última vista. Si se adjuntó la última vista, considero que es el momento para cargar más contenido.

 class MyListener implements RecyclerView.OnChildAttachStateChangeListener { RecyclerView mRecyclerView; MyListener(RecyclerView view) { mRecyclerView = view; } @Override public void onChildViewAttachedToWindow(View view) { RecyclerView.Adapter adapter = mRecyclerView.getAdapter(); RecyclerView.LayoutManager mgr = mRecyclerView.getLayoutManager(); int adapterPosition = mgr.getPosition(view); if (adapterPosition == adapter.getItemCount() - 1) { // last view was attached loadMoreContent(); } @Override public void onChildViewDetachedFromWindow(View view) {} } 

OK, lo hice usando el método onBindViewHolder de RecyclerView.Adapter.

Adaptador:

 public interface OnViewHolderListener { void onRequestedLastItem(); } @Override public void onBindViewHolder(ViewHolder holder, int position) { ... if (position == getItemCount() - 1) onViewHolderListener.onRequestedLastItem(); } 

Fragmento (o Actividad) :

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { contentView = inflater.inflate(R.layout.comments_list, container, false); recyclerView = (RecyclerView) mContentView.findViewById(R.id.my_recycler_view); adapter = new Adapter(); recyclerView.setAdapter(adapter); ... adapter.setOnViewHolderListener(new Adapter.OnViewHolderListener() { @Override public void onRequestedLastItem() { //TODO fetch new data from webservice } }); return contentView; } 

Intentaré extender LayoutManager usado (por ejemplo, LinearLayoutManager ) y reemplazar el método scrollVerticallyBy() . En primer lugar, llamaría a super primero y luego verificaría el valor entero devuelto. Si el valor es igual a 0 , se llega a un borde inferior o superior. Luego utilizaría el método findLastVisibleItemPosition() para averiguar qué frontera se alcanza y cargar más datos si es necesario. Solo una idea.

Además, incluso puede devolver su valor desde ese método, permitiendo el desbordamiento y mostrando el indicador de “carga”.

onBindViewHolder una implementación de tipo de desplazamiento infinito usando esta lógica en el método onBindViewHolder de mi clase RecyclerView.Adapter .

  if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) { if (mCurrentPage == mTotalPageCount) { mLoadImagesListener.noMorePages(); } else { int newPage = mCurrentPage + 1; mLoadImagesListener.loadPage(newPage); } } 

Con este código cuando RecyclerView llega al último elemento, incrementa la página actual y las devoluciones de llamada en una interfaz que es responsable de cargar más datos de la API y agregar los nuevos resultados al adaptador.

Puedo publicar un ejemplo más completo si esto no está claro?

Para las personas que usan StaggeredGridLayoutManager aquí está mi implementación, funciona para mí.

  private class ScrollListener extends RecyclerView.OnScrollListener { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { firstVivisibleItems = mLayoutManager.findFirstVisibleItemPositions(firstVivisibleItems); if(!recyclerView.canScrollVertically(1) && firstVivisibleItems[0]!=0) { loadMoreImages(); } } private boolean loadMoreImages(){ Log.d("myTag", "LAST-------HERE------"); return true; } } 

Hay una biblioteca fácil de usar para esta paginación llamada. Admite ListView y RecyclerView (LinearLayout, GridLayout y StaggeredGridLayout).

Aquí está el enlace al proyecto en Github

Con el poder de las funciones de extensión de Kotlin, el código puede parecer mucho más elegante. Coloque esto en cualquier lugar que desee (lo tengo dentro de un archivo ExtensionFunctions.kt):

 /** * WARNING: This assumes the layout manager is a LinearLayoutManager */ fun RecyclerView.addOnScrolledToEnd(onScrolledToEnd: () -> Unit){ this.addOnScrollListener(object: RecyclerView.OnScrollListener(){ private val VISIBLE_THRESHOLD = 5 private var loading = true private var previousTotal = 0 override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { with(layoutManager as LinearLayoutManager){ val visibleItemCount = childCount val totalItemCount = itemCount val firstVisibleItem = findFirstVisibleItemPosition() if (loading && totalItemCount > previousTotal){ loading = false previousTotal = totalItemCount } if(!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)){ onScrolledToEnd() loading = true } } } }) } 

Y luego úsalo así:

 youRecyclerView.addOnScrolledToEnd { //What you want to do once the end is reached } 

Esta solución se basa en la respuesta de Kushal Sharma. Sin embargo, esto es un poco mejor porque:

  1. Utiliza onScrollStateChanged lugar de onScroll . Esto es mejor porque se llama a onScroll cada vez que hay algún tipo de movimiento en el RecyclerView, mientras que onScrollStateChanged solo se onScrollStateChanged cuando se cambia el estado de RecyclerView. El uso de onScrollStateChanged le ahorrará tiempo de CPU y, como consecuencia, batería.
  2. Dado que esto utiliza funciones de extensión, esto se puede utilizar en cualquier RecyclerView que tenga. El código del cliente es solo 1 línea.

Tengo un ejemplo bastante detallado de cómo paginar con un RecyclerView. En un nivel alto, tengo un conjunto PAGE_SIZE, digamos 30. Entonces solicito 30 artículos y si obtengo 30, entonces solicito la página siguiente. Si obtengo menos de 30 elementos, marque una variable para indicar que se ha alcanzado la última página y luego dejaré de solicitar más páginas. Compruébalo y cuéntame lo que piensas.

https://medium.com/@etiennelawlor/pagination-with-recyclerview-1cb7e66a502b

Aquí mi solución que utiliza AsyncListUtil , en la web dice: Tenga en cuenta que esta clase utiliza una única cadena para cargar los datos, por lo que es adecuada para cargar datos desde el almacenamiento secundario, como el disco, pero no desde la red. pero estoy usando odata para leer los datos y funcionar bien. Extraño mis ejemplos de entidades de datos y métodos de red. Incluyo solo el adaptador de ejemplo.

 public class AsyncPlatoAdapter extends RecyclerView.Adapter { private final AsyncPlatoListUtil mAsyncListUtil; private final MainActivity mActivity; private final RecyclerView mRecyclerView; private final String mFilter; private final String mOrderby; private final String mExpand; public AsyncPlatoAdapter(String filter, String orderby, String expand, RecyclerView recyclerView, MainActivity activity) { mFilter = filter; mOrderby = orderby; mExpand = expand; mRecyclerView = recyclerView; mActivity = activity; mAsyncListUtil = new AsyncPlatoListUtil(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()). inflate(R.layout.plato_cardview, parent, false); // Create a ViewHolder to find and hold these view references, and // register OnClick with the view holder: return new PlatoViewHolderAsync(itemView, this); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { final Plato item = mAsyncListUtil.getItem(position); PlatoViewHolderAsync vh = (PlatoViewHolderAsync) holder; if (item != null) { Integer imagen_id = item.Imagen_Id.get(); vh.getBinding().setVariable(BR.plato, item); vh.getBinding().executePendingBindings(); vh.getImage().setVisibility(View.VISIBLE); vh.getProgress().setVisibility(View.GONE); String cacheName = null; String urlString = null; if (imagen_id != null) { cacheName = String.format("imagenes/imagen/%d", imagen_id); urlString = String.format("%s/menusapi/%s", MainActivity.ROOTPATH, cacheName); } ImageHelper.downloadBitmap(mActivity, vh.getImage(), vh.getProgress(), urlString, cacheName, position); } else { vh.getBinding().setVariable(BR.plato, item); vh.getBinding().executePendingBindings(); //show progress while loading. vh.getImage().setVisibility(View.GONE); vh.getProgress().setVisibility(View.VISIBLE); } } @Override public int getItemCount() { return mAsyncListUtil.getItemCount(); } public class AsyncPlatoListUtil extends AsyncListUtil { /** * Creates an AsyncListUtil. */ public AsyncPlatoListUtil() { super(Plato.class, //my data class 10, //page size new DataCallback() { @Override public int refreshData() { //get count calling ../$count ... odata endpoint return countPlatos(mFilter, mOrderby, mExpand, mActivity); } @Override public void fillData(Plato[] data, int startPosition, int itemCount) { //get items from odata endpoint using $skip and $top Platos p = loadPlatos(mFilter, mOrderby, mExpand, startPosition, itemCount, mActivity); for (int i = 0; i < Math.min(itemCount, p.value.size()); i++) { data[i] = p.value.get(i); } } }, new ViewCallback() { @Override public void getItemRangeInto(int[] outRange) { //i use LinearLayoutManager in the RecyclerView LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager(); outRange[0] = layoutManager.findFirstVisibleItemPosition(); outRange[1] = layoutManager.findLastVisibleItemPosition(); } @Override public void onDataRefresh() { mRecyclerView.getAdapter().notifyDataSetChanged(); } @Override public void onItemLoaded(int position) { mRecyclerView.getAdapter().notifyItemChanged(position); } }); mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { onRangeChanged(); } }); } } } 

@kushal @abdulaziz

Why not use this logic instead?

 public void onScrolled(RecyclerView recyclerView, int dx, int dy) { int totalItemCount, lastVisibleItemPosition; if (dy > 0) { totalItemCount = _layoutManager.getItemCount(); lastVisibleItemPosition = _layoutManager.findLastVisibleItemPosition(); if (!_isLastItem) { if ((totalItemCount - 1) == lastVisibleItemPosition) { LogUtil.e("end_of_list"); _isLastItem = true; } } } } 

Try below:

 import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.LayoutManager; /** * Abstract Endless ScrollListener * */ public abstract class EndlessScrollListener extends RecyclerView.OnScrollListener { // The minimum amount of items to have below your current scroll position // before loading more. private int visibleThreshold = 10; // The current offset index of data you have loaded private int currentPage = 1; // True if we are still waiting for the last set of data to load. private boolean loading = true; // The total number of items in the data set after the last load private int previousTotal = 0; private int firstVisibleItem; private int visibleItemCount; private int totalItemCount; private LayoutManager layoutManager; public EndlessScrollListener(LayoutManager layoutManager) { validateLayoutManager(layoutManager); this.layoutManager = layoutManager; } public EndlessScrollListener(int visibleThreshold, LayoutManager layoutManager, int startPage) { validateLayoutManager(layoutManager); this.visibleThreshold = visibleThreshold; this.layoutManager = layoutManager; this.currentPage = startPage; } private void validateLayoutManager(LayoutManager layoutManager) throws IllegalArgumentException { if (null == layoutManager || !(layoutManager instanceof GridLayoutManager) || !(layoutManager instanceof LinearLayoutManager)) { throw new IllegalArgumentException( "LayoutManager must be of type GridLayoutManager or LinearLayoutManager."); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); visibleItemCount = recyclerView.getChildCount(); totalItemCount = layoutManager.getItemCount(); if (layoutManager instanceof GridLayoutManager) { firstVisibleItem = ((GridLayoutManager) layoutManager) .findFirstVisibleItemPosition(); } else if (layoutManager instanceof LinearLayoutManager) { firstVisibleItem = ((LinearLayoutManager) layoutManager) .findFirstVisibleItemPosition(); } if (loading) { if (totalItemCount > previousTotal) { loading = false; previousTotal = totalItemCount; } } if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) { // End has been reached do something currentPage++; onLoadMore(currentPage); loading = true; } } // Defines the process for actually loading more data based on page public abstract void onLoadMore(int page); } 

I have created LoadMoreRecyclerView using Abdulaziz Noor Answer

LoadMoreRecyclerView

 public class LoadMoreRecyclerView extends RecyclerView { private boolean loading = true; int pastVisiblesItems, visibleItemCount, totalItemCount; //WrapperLinearLayout is for handling crash in RecyclerView private WrapperLinearLayout mLayoutManager; private Context mContext; private OnLoadMoreListener onLoadMoreListener; public LoadMoreRecyclerView(Context context) { super(context); mContext = context; init(); } public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mContext = context; init(); } public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mContext = context; init(); } private void init(){ mLayoutManager = new WrapperLinearLayout(mContext,LinearLayoutManager.VERTICAL,false); this.setLayoutManager(mLayoutManager); this.setItemAnimator(new DefaultItemAnimator()); this.setHasFixedSize(true); } @Override public void onScrolled(int dx, int dy) { super.onScrolled(dx, dy); if(dy > 0) //check for scroll down { visibleItemCount = mLayoutManager.getChildCount(); totalItemCount = mLayoutManager.getItemCount(); pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition(); if (loading) { if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount) { loading = false; Log.v("...", "Call Load More !"); if(onLoadMoreListener != null){ onLoadMoreListener.onLoadMore(); } //Do pagination.. ie fetch new data } } } } @Override public void onScrollStateChanged(int state) { super.onScrollStateChanged(state); } public void onLoadMoreCompleted(){ loading = true; } public void setMoreLoading(boolean moreLoading){ loading = moreLoading; } public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) { this.onLoadMoreListener = onLoadMoreListener; } } 

WrapperLinearLayout

 public class WrapperLinearLayout extends LinearLayoutManager { public WrapperLinearLayout(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { try { super.onLayoutChildren(recycler, state); } catch (IndexOutOfBoundsException e) { Log.e("probe", "meet a IOOBE in RecyclerView"); } } } 

//Add it in xml like

   

OnCreate or onViewCreated

 mLoadMoreRecyclerView = (LoadMoreRecyclerView) view.findViewById(R.id.recycler_view); mLoadMoreRecyclerView.setOnLoadMoreListener(new OnLoadMoreListener() { @Override public void onLoadMore() { callYourService(StartIndex); } }); 

callYourService

 private void callYourService(){ //callyour Service and get response in any List List newDataFromServer = getDataFromServerService(); //Enable Load More mLoadMoreRecyclerView.onLoadMoreCompleted(); if(newDataFromServer != null && newDataFromServer.size() > 0){ StartIndex += newDataFromServer.size(); if (newDataFromServer.size() < Integer.valueOf(MAX_ROWS)) { //StopLoading.. mLoadMoreRecyclerView.setMoreLoading(false); } } else{ mLoadMoreRecyclerView.setMoreLoading(false); mAdapter.notifyDataSetChanged(); } } 

This is how I do it, simple and short:

  recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if(!recyclerView.canScrollVertically(1) && dy != 0) { // Load more results here } } }); 
  recyclerList.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx,int dy) { super.onScrolled(recyclerView, dx, dy); } @Override public void onScrollStateChanged(RecyclerView recyclerView,int newState) { int totalItemCount = layoutManager.getItemCount(); int lastVisibleItem = layoutManager.findLastVisibleItemPosition(); if (totalItemCount> 1) { if (lastVisibleItem >= totalItemCount - 1) { // End has been reached // do something } } } }); 

Check this every thing is explained in detail: Pagination using RecyclerView From A to Z

  mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int visibleItemCount = mLayoutManager.getChildCount(); int totalItemCount = mLayoutManager.getItemCount(); int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition(); if (!mIsLoading && !mIsLastPage) { if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount && firstVisibleItemPosition >= 0) { loadMoreItems(); } } } }) 

loadMoreItems():

 private void loadMoreItems() { mAdapter.removeLoading(); //load data here from the server // in case of success mAdapter.addData(data); // if there might be more data mAdapter.addLoading(); } 

in MyAdapter :

 private boolean mIsLoadingFooterAdded = false; public void addLoading() { if (!mIsLoadingFooterAdded) { mIsLoadingFooterAdded = true; mLineItemList.add(new LineItem()); notifyItemInserted(mLineItemList.size() - 1); } } public void removeLoading() { if (mIsLoadingFooterAdded) { mIsLoadingFooterAdded = false; int position = mLineItemList.size() - 1; LineItem item = mLineItemList.get(position); if (item != null) { mLineItemList.remove(position); notifyItemRemoved(position); } } } public void addData(List data) { for (int i = 0; i < data.size(); i++) { YourDataClass yourDataObject = data.get(i); mLineItemList.add(new LineItem(yourDataObject)); notifyItemInserted(mLineItemList.size() - 1); } } 

None of these answers take into account if the list is too small or not.

Here’s a piece of code I’ve been using that works on RecycleViews in both directions.

 @Override public boolean onTouchEvent(MotionEvent motionEvent) { if (recyclerViewListener == null) { return super.onTouchEvent(motionEvent); } /** * If the list is too small to scroll. */ if (motionEvent.getAction() == MotionEvent.ACTION_UP) { if (!canScrollVertically(1)) { recyclerViewListener.reachedBottom(); } else if (!canScrollVertically(-1)) { recyclerViewListener.reachedTop(); } } return super.onTouchEvent(motionEvent); } public void setListener(RecyclerViewListener recycleViewListener) { this.recyclerViewListener = recycleViewListener; addOnScrollListener(new OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (recyclerViewListener == null) { return; } recyclerViewListener.scrolling(dy); if (!canScrollVertically(1)) { recyclerViewListener.reachedBottom(); } else if (!canScrollVertically(-1)) { recyclerViewListener.reachedTop(); } } }); } 

I let you my aproximation. Funciona bien para mí

Espero que te ayude.

 /** * Created by Daniel Pardo Ligorred on 03/03/2016. */ public abstract class BaseScrollListener extends RecyclerView.OnScrollListener { protected RecyclerView.LayoutManager layoutManager; public BaseScrollListener(RecyclerView.LayoutManager layoutManager) { this.layoutManager = layoutManager; this.init(); } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); this.onScroll(recyclerView, this.getFirstVisibleItem(), this.layoutManager.getChildCount(), this.layoutManager.getItemCount(), dx, dy); } private int getFirstVisibleItem(){ if(this.layoutManager instanceof LinearLayoutManager){ return ((LinearLayoutManager) this.layoutManager).findFirstVisibleItemPosition(); } else if (this.layoutManager instanceof StaggeredGridLayoutManager){ int[] spanPositions = null; //Should be null -> StaggeredGridLayoutManager.findFirstVisibleItemPositions makes the work. try{ return ((StaggeredGridLayoutManager) this.layoutManager).findFirstVisibleItemPositions(spanPositions)[0]; }catch (Exception ex){ // Do stuff... } } return 0; } public abstract void init(); protected abstract void onScroll(RecyclerView recyclerView, int firstVisibleItem, int visibleItemCount, int totalItemCount, int dx, int dy); } 

It is also possible to implement without the scroll listener, using the pure logic of the data model alone. The scroll view requires to get items by position as well as the maximal item count. The model can have the background logic to fetch the needed items in chunks, rather than one by one, and do this in the background thread, notifying the view when the data are ready.

This approach allows to have the fetching queue which prefers most recently requested (so currently visible) items over older (most likely already scrolled away) submissions, control the number of parallel threads to use and things the like. The complete source code for this approach (demo app and reusable library) are available here .

This solution works perfectly for me.

 //Listener public abstract class InfiniteScrollListener extendsRecyclerView.OnScrollListener { public static String TAG = InfiniteScrollListener.class.getSimpleName(); int firstVisibleItem, visibleItemCount, totalItemCount; private int previousTotal = 0; private boolean loading = true; private int visibleThreshold = 1; private int current_page = 1; private LinearLayoutManager mLinearLayoutManager; public InfiniteScrollListener(LinearLayoutManager linearLayoutManager) { this.mLinearLayoutManager = linearLayoutManager; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); visibleItemCount = recyclerView.getChildCount(); totalItemCount = mLinearLayoutManager.getItemCount(); firstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition(); if (loading) { if (totalItemCount > previousTotal) { loading = false; previousTotal = totalItemCount; } } if (!loading && (totalItemCount - visibleItemCount - firstVisibleItem <= visibleThreshold)) { current_page++; onLoadMore(current_page); loading = true; } } public void resetState() { loading = true; previousTotal = 0; current_page = 1; } public abstract void onLoadMore(int current_page); } //Implementation into fragment private InfiniteScrollListener scrollListener; scrollListener = new InfiniteScrollListener(manager) { @Override public void onLoadMore(int current_page) { //Load data } }; rv.setLayoutManager(manager); rv.addOnScrollListener(scrollListener); 

@erdna Please refer my below code.May be it will become helpful to you.

  int firstVisibleItem, visibleItemCount, totalItemCount; recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); visibleItemCount = layoutManager.getChildCount(); totalItemCount = layoutManager.getItemCount(); firstVisibleItem = layoutManager.findFirstVisibleItemPosition(); Log.e("firstVisibleItem", firstVisibleItem + ""); Log.e("visibleItemCount", visibleItemCount + ""); Log.e("totalItemCount", totalItemCount + ""); if (page != total_page_index) { if (loading) { if ((visibleItemCount + firstVisibleItem) >= totalItemCount) { Log.e("page", String.valueOf(page)); page=page+1; new GetSummary().execute(String.valueOf(page), ""); loading = false; } } } } }); 
 if (layoutManager.findLastCompletelyVisibleItemPosition() == recyclerAdapter.getItemCount() - 1) { //load more items. } 

Fair and simple. Esto funcionará