recyclerview more incremental android listview android-recyclerview material-design onscrolllistener

android - more - ¿Cómo implementar una lista interminable con RecyclerView?



pagination android (30)

He creado LoadMoreRecyclerView utilizando 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.. i.e. 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"); } } }

// Añadirlo en xml como

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

OnCreate o onViewCreated

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

llame a su servicio

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

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

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


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

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

Para una comprensió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<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(); } }; } }


Aquí mi solución que utiliza 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 datos desde un almacenamiento secundario como el disco, pero no desde la red. pero estoy usando odata para leer los datos y trabajar bien. Extraño en mi ejemplo las entidades de datos y los 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<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(); } }); } } }


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

Le sugiero que eche un vistazo a este artículo con recomendaciones breves pero valiosas sobre la creación de administradores de Layout personalizados: http://cases.azoft.com/create-custom-layoutmanager-android/


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


Con el poder de las funciones de extensión de Kotlin, el código puede parecer mucho más elegante. Coloca esto donde quieras (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 RecyclerView, mientras que a onScrollStateChanged solo se llama cuando se cambia el estado de RecyclerView. El uso de onScrollStateChanged le ahorrará tiempo de CPU y, como consecuencia, la batería.
  2. Como esto utiliza funciones de extensión, esto se puede usar en cualquier RecyclerView que tenga. El código del cliente es solo 1 línea.

Gracias a @Kushal y así 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.. i.e. fetch new data } } } } });

No olvides añadir

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


Hay una biblioteca fácil de usar para esta página nombrada. Admite tanto ListView como RecyclerView (LinearLayout, GridLayout y StaggeredGridLayout).

Aquí está el github.com/MarkoMilos/Paginate al proyecto en Github.


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 que está 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 pergaminos 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 .

Ya que Android ahora es oficialmente compatible con 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 tu RecyclerView como este

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

Para un ejemplo de código completo, no dude en consultar este repositorio de Github .


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

Además, incluso puede devolver su valor de ese método permitiendo el desplazamiento excesivo y mostrando el indicador de "carga".


La mayoría de las respuestas se LinearLayoutManager el supuesto de que 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" a sus contenidos. Debería ser la Vista del reciclador (que es la única clase responsable de lo que se muestra actualmente al usuario, y no el adaptador que solo es responsable de 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 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. } }


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


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

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


OK, lo hice utilizando 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; }


Para aquellos que solo desean recibir una notificación cuando el último elemento se muestra totalmente, 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 utilizar recyclerView.getLayoutManager().canScrollVertically() si desea admitir API <14.


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


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: mLoading debe cambiarse al final de los trabajos asíncronos. ¡Espero que sea de ayuda!


Tengo un ejemplo bastante detallado de cómo paginar con un RecyclerView. En un nivel alto, tengo un conjunto PAGE_SIZE, digamos 30. Por lo tanto, solicito 30 artículos y, si vuelvo a obtener 30, solicito la página siguiente. Si obtengo menos de 30 elementos, señalo una variable para indicar que se ha alcanzado la última página y luego dejo de solicitar más páginas. Compruébalo y déjame saber lo que piensas.

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


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 desde la API y de agregar los nuevos resultados al adaptador.

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


Así es como lo hago, simple y corto:

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


@erdna Por favor, consulte mi código a continuación. Puede ser que sea útil para usted.

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


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


Esta solución funciona perfectamente para mí.

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


Ninguna de estas respuestas tiene en cuenta si la lista es demasiado pequeña o no.

Aquí hay un fragmento de código que he estado usando que funciona en RecycleViews en ambas direcciones.

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


Pruebe a continuación:

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


Compruebe que todo esto se explica en detalle: Paginación usando RecyclerView De la A a la 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(); }

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


También es posible implementarlo sin el detector de desplazamiento, utilizando solo la lógica pura del modelo de datos. La vista de desplazamiento requiere obtener elementos por posición, así como el recuento máximo de elementos. El modelo puede tener la lógica de fondo para recuperar los elementos necesarios en trozos, en lugar de uno por uno, y hacer esto en el hilo de fondo, notificando a la vista cuando los datos están listos.

Este enfoque permite tener la cola de recuperación que prefiere los elementos solicitados más recientemente (por lo tanto, actualmente visibles) sobre los envíos más antiguos (lo más probable es que ya se hayan desplazado), controlan la cantidad de subprocesos paralelos que se utilizan y cosas similares. El código fuente completo para este enfoque (aplicación de demostración y biblioteca reutilizable) está disponible here .


Te dejo mi aproximación. Funciona bien para mi

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


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


if (layoutManager.findLastCompletelyVisibleItemPosition() == recyclerAdapter.getItemCount() - 1) { //load more items. }

Justo y sencillo Esto funcionará.