¿Cómo implementar una lista interminable con RecyclerView?

Quisiera cambiar ListView a RecyclerView . Quiero utilizar el onScroll de OnScrollListener en RecyclerView para determinar si un usuario desplazado hasta el final de la lista.

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

Gracias a @Kushal y esto es cómo 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 añadir

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

Hacer estas variables.

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

Establecer en desplazamiento para ver 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 que está utilizando LinearLayoutManager como gestor de diseño para RecyclerView .

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

Y para una cuadrícula

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

¡Diviértase con sus pergaminos sin fin !! ^. ^

Actualización: mRecyclerView. SetOnScrollListener () está obsoleto simplemente reemplazar con mRecyclerView.addOnScrollListener() y la advertencia se habrá ido! Usted puede leer más de esta pregunta SO .

Desde Android ahora oficialmente apoyo Kotlin, aquí hay una actualización para el mismo –

Hacer OnScrollListener

 class OnScrollListener(val layoutManager: LinearLayoutManager, val adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>, val dataList: MutableList<Int>) : 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 añádelo a su RecyclerView como este

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

Para obtener un ejemplo de código completo, no dude en remitir este reporte de Github .

Para aquellos que sólo quieren ser notificados cuando el último elemento se muestra totalmente, puede utilizar 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 utilizar recyclerView.getLayoutManager().canScrollVertically() si desea admitir API <14.

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

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

Para una comprensión del detalle escribí un poste del blog y un proyecto del ejemplo consígalo aquí http://sab99r.com/blog/recyclerview-endless-load-more/

MyAdapter.java

 public abstract class MyAdapter extends RecyclerView.Adapter<ViewHolder>{ @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> 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 tenía EndlessScrollListener (que se puede encontrar fácilmente en muchas respuestas en SO) a un RecyclerView por lo 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 asincrónicos: mLoading debe cambiarse al final de los trabajos asíncronos. Espero que sea útil!

La mayoría de la respuesta está asumiendo que el 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 .

El uso del adaptador ViewHolder no es claramente una buena solución. Es posible que un adaptador tenga más de 1 RecyclerView utilice. "Adapta" su contenido. Debe ser el Recycler View (que es la clase que es responsable de lo que se muestra actualmente al usuario, y no el adaptador que es responsable sólo para proporcionar contenido al 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 abstraídas del 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 solución a continuación utiliza addOnScrollListener ya que setOnScrollListener está obsoleto, 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 muchas respuestas a la pregunta, me gustaría compartir nuestra experiencia de crear la vista de lista sin fin. Recientemente hemos implementado Custom Carousel LayoutManager que puede trabajar en el ciclo desplazando la lista infinitamente, así como hasta cierto punto. Aquí está una descripción detallada de GitHub .

Le sugiero que eche un vistazo a este artículo con recomendaciones breves pero valiosas sobre la creación de 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 ha adjuntado la última vista. Si se adjuntó la última vista, lo considero como un 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) {} } 

Aceptar, lo hice mediante 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; } 

Intento extender el LayoutManager usado (por ejemplo, LinearLayoutManager ) y sobreescribir el método scrollVerticallyBy() . En primer lugar, yo llamaría super primero y luego comprobar el valor entero devuelto. Si el valor es igual a 0 , se alcanzará un borde inferior o superior. Entonces yo usaría el método findLastVisibleItemPosition() para averiguar qué frontera se alcanzó y cargar más datos si es necesario. Solo una idea.

Además, incluso puede devolver su valor de ese método que permite supercroll y mostrar el indicador de "carga".

onBindViewHolder una implementación de tipo scrolling infinita 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 obtiene el ú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 utilizan StaggeredGridLayoutManager aquí es 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 nombrada. Soporta ListView y RecyclerView (LinearLayout, GridLayout y StaggeredGridLayout).

Aquí está el enlace al proyecto en Github

Tengo un ejemplo muy detallado de cómo paginar con un RecyclerView. En un nivel alto, tengo un conjunto PAGE_SIZE, permite decir 30. Así que solicito 30 elementos y si tengo 30 atrás, entonces solicito la siguiente página. Si obtengo menos de 30 elementos, marca una variable para indicar que se ha alcanzado la última página y luego dejo de solicitar más páginas. Compruébelo y hágamelo saber lo que usted piensa.

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

Aquí mi solución usando AsyncListUtil , en la web dice: Tenga en cuenta que esta clase utiliza un solo hilo para cargar los datos, por lo que es adecuado para cargar los datos de almacenamiento secundario, como el disco, pero no de la red. Pero estoy utilizando odata para leer los datos y trabajar bien. Extraño mis entidades de datos de ejemplo y métodos de red. Incluyo sólo 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<Plato> { /** * Creates an AsyncListUtil. */ public AsyncPlatoListUtil() { super(Plato.class, //my data class 10, //page size new DataCallback<Plato>() { @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

¿Por qué no usar esta lógica en su lugar?

 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); } 

There is a method public void setOnScrollListener (RecyclerView.OnScrollListener listener) in https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29 . Use that

EDITAR:

Override onScrollStateChanged method inside the onScrollListener and do this

  boolean loadMore = firstVisibleItem + visibleItemCount >= totalItemCount; //loading is used to see if its already loading, you have to manually manipulate this boolean variable if (loadMore && !loading) { //end of list reached } 
  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<YourDataClass> 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 esto 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); } 

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

 <your.package.LoadMoreRecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"> </your.package.LoadMoreRecyclerView> 

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<AnyModelClass> 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(); } } 

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; } } } } }); 
  • ¿Que es mejor? NotifyDataSetChanged o notifyItemChanged en el bucle?
  • ¿Cuál es la diferencia entre ListView, AbsListView y RecyclerView
  • Pasar los datos de los elementos de los clics hechos por RecyclerView CardView a la actividad
  • Android FlowLayout como RecyclerView LayoutManager
  • Establecer contentInset en RecyclerView
  • Agregar los efectos de un RippleDrawable y un StateListDrawable a un RecyclerView
  • RecyclerView y DiffUtil - Una pesadilla de concurrencia
  • Android Recyclerview lag mientras se desplaza hacia abajo
  • No contraer la barra de herramientas cuando RecyclerView se ajusta a la pantalla
  • Cómo guardar la posición de desplazamiento de RecyclerView en Android?
  • Elemento de Recyclerview efecto de ondulación de clic
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.