如何用RecyclerView实现无尽的列表?
我想将ListView
更改为RecyclerView
。 我想在RecyclerView
使用OnScrollListener
的onScroll
来确定用户是否滚动到列表的末尾。
如何知道用户是否滚动到列表的末尾,以便从REST服务中获取新数据?
感谢@Kushal,这是我实现它的方式
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 } } } } });
不要忘记添加
LinearLayoutManager mLayoutManager; mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager);
做这些variables。
private int previousTotal = 0; private boolean loading = true; private int visibleThreshold = 5; int firstVisibleItem, visibleItemCount, totalItemCount;
在Scroll上设置回收站视图。
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; } } });
注意:确保您使用
LinearLayoutManager
作为RecyclerView
布局pipe理器。
LinearLayoutManager mLayoutManager; mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager);
和网格
GridLayoutManager mLayoutManager; mLayoutManager = new GridLayoutManager(getActivity(), spanCount); mRecyclerView.setLayoutManager(mLayoutManager);
用你无尽的卷轴玩得开心! ^。^
更新: mRecyclerView。
setOnScrollListener()已被弃用,只需用mRecyclerView.addOnScrollListener()
replace,警告就会消失! 你可以阅读更多这个SO问题 。
由于Android现在正式支持Kotlin,所以这是一个相同的更新 –
使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 } } }
并将其添加到您的RecyclerView像这样
recyclerView.addOnScrollListener(OnScrollListener(layoutManager, adapter, dataList))
对于完整的代码示例,请随时引用此Github回购 。
对于那些只想在完全显示最后一个项目时得到通知的人,可以使用View.canScrollVertically()
。
这是我的实现:
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() {} }
注意:如果要支持API <14,可以使用recyclerView.getLayoutManager().canScrollVertically()
。
这是另一种方法。 它将与任何布局pipe理器一起工作。
- 使适配器类抽象
- 然后在适配器类(例如load())中创build一个抽象方法
- 在onBindViewHolder中检查位置是否最后调用load()
- 在您的活动或片段中创build适配器对象时,重写load()函数。
- 在overloaded load函数中实现你的loadmore调用
为了详细了解,我写了一篇博文和示例项目,在这里获取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(); } }; } }
我的答案是Noor的修改版本。 我从一个ListView
,我有EndlessScrollListener
(你可以很容易地在许多答案中find)传递给一个RecyclerView
所以我想要一个EndlessRecyclScrollListener
轻松更新我的以前的监听器。
所以这里是代码,希望它有帮助:
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); }
对我来说,这非常简单:
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; } } });
请注意asynchronous作业:必须在asynchronous作业结束时更改mLoading。 希望它会有帮助!
大多数答案都假定RecyclerView
使用LinearLayoutManager
, GridLayoutManager
,甚至StaggeredGridLayoutManager
,或者假定滚动是垂直的或者是横向的,但是没有人发布完全通用的答案 。
使用ViewHolder
的适配器显然不是一个好的解决scheme。 一个适配器可能有超过1个RecyclerView
使用它。 它“适应”他们的内容。 它应该是Recycler View (它是一个负责当前显示给用户的类,而不是负责仅向RecyclerView
提供内容的适配器),它必须通知你的系统需要更多的项目(加载)。
这里是我的解决scheme,除了RecyclerView的抽象类(RecycerView.LayoutManager和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. } }
虽然接受的答案完美地工作,下面的解决scheme使用addOnScrollListener,因为setOnScrollListener已被弃用,并减lessvariables的数量,如果条件。
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(); } } } });
虽然这个问题的答案如此之多,但我想分享一下我们创build无尽列表的经验。 我们最近实现了可以在循环中工作的自定义Carousel LayoutManager,通过无限滚动列表以及达到某个特定点。 这里是关于GitHub的详细描述 。
我build议你看看这篇文章,简短但有价值的build议,以创build自定义布局pipe理器: http : //cases.azoft.com/create-custom-layoutmanager-android/
我的方法来检测加载事件不是检测滚动,而是听最后的视图是否附加。 如果最后一个视图是附加的,我认为这是加载更多内容的时机。
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) {} }
好的,我使用了RecyclerView.Adapter的onBindViewHolder方法。
适配器:
public interface OnViewHolderListener { void onRequestedLastItem(); } @Override public void onBindViewHolder(ViewHolder holder, int position) { ... if (position == getItemCount() - 1) onViewHolderListener.onRequestedLastItem(); }
片段 (或活动) :
@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; }
我会尝试扩展使用的LayoutManager
(例如LinearLayoutManager
)并重写scrollVerticallyBy()
方法。 首先,我会先调用super
,然后检查返回的整数值。 如果该值等于0
则达到底部或顶部边界。 然后,我会使用findLastVisibleItemPosition()
方法来找出到达哪个边界,并根据需要加载更多的数据。 只是一个想法。
此外,你甚至可以从这个方法返回你的价值,允许overscroll和显示“加载”指标。
我在我的RecyclerView.Adapter
类的onBindViewHolder
方法中使用这个逻辑实现了无限的滚动types实现。
if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) { if (mCurrentPage == mTotalPageCount) { mLoadImagesListener.noMorePages(); } else { int newPage = mCurrentPage + 1; mLoadImagesListener.loadPage(newPage); } }
有了这个代码,当RecyclerView到达最后一个项目,它增加了一个接口上的当前页面和callback,负责从API加载更多的数据,并将新的结果添加到适配器。
如果不清楚,我可以发布更完整的例子吗?
对于使用StaggeredGridLayoutManager的人来说,这是我的实现,它适用于我。
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; } }
这个命名分页有一个易于使用的库。 支持ListView和RecyclerView(LinearLayout,GridLayout和StaggeredGridLayout)。
这里是Github项目的链接
我有一个非常详细的例子,如何与RecyclerView分页。 在高层次上,我有一个PAGE_SIZE,可以说30。所以我要求30项,如果我得到30回,那么我请求下一页。 如果我得到less于30个项目,我会标记一个variables来表明最后一页已经到达,然后我停止请求更多的页面。 检查出来,让我知道你的想法。
https://medium.com/@etiennelawlor/pagination-with-recyclerview-1cb7e66a502b
在这里我的解决scheme使用AsyncListUtil ,在网上说:请注意,这个类使用单线程来加载数据,所以它适合从辅助存储,如磁盘,而不是从networking加载数据。 但我使用odata来读取数据并正常工作。 我想我的例子中的数据实体和networking方法。 我只包含示例适配器。
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
为什么不用这个逻辑呢?
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; } } } }
试试下面:
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
<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(); } }
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. Works fine for me.
我希望它可以帮助你。
/** * 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; } } } } });
With the power of Kotlin's extension functions, the code can look a lot more elegant. Put this anywhere you want (I have it inside an ExtensionFunctions.kt file):
/** * 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 } } } }) }
然后像这样使用它:
youRecyclerView.addOnScrolledToEnd { //What you want to do once the end is reached }
This solution is based on Kushal Sharma's answer. However, this is a bit better because:
- It uses
onScrollStateChanged
instead ofonScroll
. This is better becauseonScroll
is called every time there is any sort of movement in the RecyclerView, whereasonScrollStateChanged
is only called when the state of the RecyclerView is changed. UsingonScrollStateChanged
will save you CPU time and, as a consequence, battery. - Since this uses Extension Functions, this can be used in any RecyclerView you have. The client code is just 1 line.
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
编辑:
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 }