ListView
更改为RecyclerView
。我想使用onScroll
中OnScrollListener
的RecyclerView
来确定用户是否滚动到列表的末尾。 我如何知道用户是否滚动到列表的末尾,以便我可以从REST服务中获取新数据?
#1 楼
感谢@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.. i.e. fetch new data
loading = true;
}
}
}
}
});
别忘了添加
LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
评论
我可以为StaggeredGridLayoutManager做些什么
– Pratik Butani
15年4月8日在10:27
怎么样mLayoutManager.findLastCompletelyVisibleItemPosition()== mLayoutManager.getItemCount()-1
–laaptu
15年4月16日在17:43
对于所有未解决findFirstVisibleItemPosition()的人,应将RecyclerView.LayoutManager更改为LinearLayoutManager
– Amr Mohammed
15年6月18日在14:17
再次使load = true的条件在哪里?
–巴尔加夫
2015年12月11日,12:57
不要忘了在//分页部分之后添加loading = true。否则,此代码将仅运行一次,并且您无法加载更多项目。
– Shaishav Jogani
16年7月20日在10:12
#2 楼
设置这些变量。private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;
在滚动条上设置以查看回收者视图。
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
的布局管理器。LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
和网格
GridLayoutManager mLayoutManager;
mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
mRecyclerView.setLayoutManager(mLayoutManager);
与无尽的滚动乐趣! ^。^
更新:不建议使用mRecyclerView.setOnScrollListener(),只需将其替换为
mRecyclerView.addOnScrollListener()
,警告将消失!您可以从这个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回购。
评论
是否知道如何通过GridLayoutManager应用此解决方案?由于findFirstVisibleItemPosition()方法仅在LinearLayoutManager中可用,因此我不知道如何使其工作。
–MathieuMaree
2014年11月16日19:47
附加信息:在这种情况下,使用visibleItemCount = mLayoutManager.getChildCount();。行为与使用visibleItemCount = mRecyclerView.getChildCount();相同。
–李静
2014年11月19日在9:10
将代码放在onScrollStateChanged()方法而不是onScrolled()方法中。
–阿尼鲁德(Anirudh)
2014年11月26日下午6:26
@ toobsco42您可以使用以下方法:int [] firstVisibleItemPositions = new int [yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
–MathieuMaree
2015年2月27日在13:55
这是什么findFirstVisibleItemPosition()无法解析
– Suresh Sharma
15年6月16日在8:54
#3 楼
对于只希望在最后一个项目完全显示时获得通知的用户,可以使用View.canScrollVertically()
。这是我的实现: >注意:如果要支持API <14,可以使用
recyclerView.getLayoutManager().canScrollVertically()
。评论
这是一个非常简单的答案!谢谢!
– Andranik
2015年5月31日14:40
通过检查!recyclerView.canScrollVertically(-1),可以使用类似的解决方案进行刷新。
– LordParsley
15年7月27日在12:54
最好的解决方案。多谢,伙计!
–安格玛
2015年9月15日15:45在
@LordParsley谢谢,更新。这也是SwipeRefreshLayout检查拉动刷新的方式之一。
–张海
2015年10月5日,下午3:07
@EpicPandaForce如果您使用的是RecyclerView,您将再次幸运,因为您可以简单地制作View.canScrollVertically()的静态副本,因为相关方法被标记为public,而不是在RecyclerView中受保护。如果愿意,还可以扩展RecyclerView以重新实现canScrollVertically()。第三种简单方法是调用recyclerView.getLayoutManager()。canScrollVertically()。编辑我的答案。
–张海
2015年11月26日12:04
#4 楼
这是另一种方法。它将与任何布局管理器一起使用。将Adapter类抽象为
然后在适配器类中创建一个抽象方法(例如load())
在onBindViewHolder中检查如果最后一个位置并调用load()
,则在您的活动或片段中创建适配器对象时覆盖load()函数。
在上面的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();
}
};
}
}
评论
像解决方案一样,您不必使适配器抽象,而是可以在适配器中创建接口,并使您的活动实现该接口,以使其灵活,美观,例如:ItemDisplayListener => onItemDisplayed(int position)。这样,您可以在任何位置加载:N,N-1,N-2。
– Samer
15年8月27日在5:12
@mcwise添加一个布尔完成= false;当您到达末尾时,使finish = true ..将当前的if条件更改为if((position> = getItemCount()-1)&&(!finished))
– Sabeer Mohammed
2015年12月7日13:48
这应该标记为正确答案。这是优雅而简单的。
–格雷格·恩尼斯(Greg Ennis)
15/12/22在0:36
一个优秀的解决方案很好的例子,它埋在第一个解决方案的流行之下。通常,最后一行将绑定到单独的视图类型,该视图类型用作“正在加载...”横幅。当此视图类型获得绑定调用时,它将设置一个回调,当更多数据可用时,该回调将触发数据更改通知。连接滚动侦听器的公认答案很浪费,并且可能不适用于其他布局类型。
–迈克尔·海斯(Michael Hays)
16年2月24日在0:59
@mcwise仅在用户主动滚动并返回到最后一项时才会无休止地调用。每次视图进入屏幕时,只会调用一次onBindViewHolder,并且不会不断调用它。
–刘大卫
16年3月18日在22:55
#5 楼
我的答案是Noor的修改版。我从一个有ListView
(可以在SO上找到很多答案的地方)的EndlessScrollListener
传递到一个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);
}
评论
您的实现要求RecyclerView使用LinearLeayoutManager。馊主意!
– Orri
16年7月4日在9:02
@Orri请您解释为什么?谢谢!
– Federico Ponzi
16年7月4日在9:06
在onScrolled中,将LayoutManager从RecyclerView投射到LinearLayoutManager。它不适用于StaggredGridLayout或其他。 StaggredGridLayout中没有findFirstVisibleItemPosition()。
– Orri
16年7月4日在16:01
@Orri感谢您指出这一点!但我不认为这是一个坏主意,因为它对我有用,并且根据支持意见,还可以处理其他情况:)
– Federico Ponzi
16年7月22日在8:50
#6 楼
对我来说,这很简单: 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;
}
}
});
注意异步作业:必须在异步作业结束时更改mLoading。希望对您有所帮助!
评论
经过几天的自我质疑,我基本上找到了这个答案。这是最好的解决方案,imo。
–wsgeorge
15年11月26日在15:06
请给通过loadmore页面编号通过android的recyclerview解决方案
– Harsha
16年7月22日在8:05
getWebServiceData(); mStoresAdapterData.setOnLoadMoreListener(new StoresAdapter.OnLoadMoreListener(){@Override public void onLoadMore(){//添加null,因此适配器将检查view_type并在底部storeList.add(null)处显示进度栏; mStoresAdapterData.notifyItemInserted(storesList.size ()-1); ++ pageNumber; getWebServiceData();}});都一个接一个地调用,最后只有第二页数据可见,请帮助
– Harsha
16年8月23日在11:52
我尝试了实现recyclerView分页的许多方法,但均无济于事,除了这一步,您有我的支持。
–穆罕默德·埃拉希德(Mohammed Elrashied)
17年2月25日在17:24
#7 楼
大多数答案是假设RecyclerView
使用LinearLayoutManager
或GridLayoutManager
甚至StaggeredGridLayoutManager
,或者假定滚动是垂直或水平滚动,但没有人发布完全完整的答案。使用
ViewHolder
的适配器显然不是一个好的解决方案。一个适配器可能使用了1个以上的RecyclerView
。它“适应”其内容。应该是RecyclerView(这是负责当前显示给用户的一类,而不是仅负责向RecyclerView
提供内容的适配器),它必须通知您的系统需要更多物品(以这是我的解决方案,仅使用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.
}
}
评论
您的答案是正确的,并且适用于所有LayoutManager,但仅适用于一小会儿:将计算代码移至另一个scrollListener方法中-将其移至onScrollStateChanged而不是onScrolled。然后在onScrollStateChanged中检查:if(newState == RecyclerView.SCROLL_STATE_IDLE){//计算代码}
–Trancer
17-2-16在19:53
感谢您的建议,但我认为这不是更好的解决方案。开发人员希望在recyclerview停止滚动之前就触发其侦听器。因此,它可能会完成加载新数据项(如果数据存储在本地),或者甚至在recyclerview的末尾添加进度条视图持有人,因此当recyclerview停止滚动时,它可能处于循环状态-bar作为最后一个可见项目。
– Yaropro
17-2-18在20:03
#8 楼
借助Kotlin扩展功能的强大功能,代码看起来更加优雅。将此放置在所需的任何位置(我将其放在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
}
}
}
})
}
然后像这样使用它:
youRecyclerView.addOnScrolledToEnd {
//What you want to do once the end is reached
}
此解决方案基于Kushal Sharma的回答。但是,这样做会更好一点,因为:
它使用
onScrollStateChanged
而不是onScroll
。这样做更好,因为每次RecyclerView中发生任何形式的移动时都会调用onScroll
,而仅当RecyclerView的状态更改时才调用onScrollStateChanged
。使用onScrollStateChanged
将节省CPU时间,并因此节省电池。由于使用扩展功能,因此可以在您拥有的任何RecyclerView中使用。客户端代码只有1行。
评论
当我完成Pull SwipeRefreshLayout的setOnRefreshListener时,addOnScrolledToEnd无法正常运行私有内部类PullToRefresh:SwipeRefreshLayout.OnRefreshListener {重写fun onRefresh(){pbViewMore !!。visibility = View.GONE pageCount = 0 courseList.clear()//在此处调用asynctask srlNoMessageRefre !.isRefreshing =假swipeRefreshLayout !!。isRefreshing =假}}
–Naveen Kumar M
18年2月20日在7:50
#9 楼
尽管可以接受的答案非常完美,但是由于不建议使用setOnScrollListener,因此下面的解决方案使用addOnScrollListener,并减少了变量的数量和条件。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();
}
}
}
});
评论
有时,我的loadMore()方法在onScrolled()中称为thrice,不知道为什么它被称为thrice以及如何停止多次调用它。
– Ved
16年8月25日在6:23
dy> 0始终为假
–苏珊(Dushyant Suthar)
17年2月22日在13:15
@ved这是因为滚动具有3种状态:滚动,滚动,空闲。您选择一个合适的。
–TheRealChx101
18年2月24日在1:18
#10 楼
尽管这个问题有很多答案,但我想分享我们创建无尽列表视图的经验。我们最近实现了自定义的Carousel LayoutManager,可以通过无限滚动列表以及将列表滚动到某个点来循环工作。这是在GitHub上的详细说明。我建议您看一下这篇文章,其中包含有关创建自定义LayoutManager的简短但有价值的建议:http://cases.azoft.com/create-custom-layoutmanager -android /
#11 楼
这就是我的操作方式,简单而简短: 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
}
}
});
#12 楼
好的,我使用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;
}
评论
依靠onBind并不是一个好主意。 RecyclerView确实可以缓存视图+具有一些布局管理器,可以预缓存事物。我建议设置一个滚动侦听器,当滚动侦听器停止时,从布局管理器获取最后一个可见的项目位置。如果您使用的是默认布局管理器,则它们都具有findLastVisible **方法。
– Yigit
14-10-24在18:21
@yigit RecyclerView将尝试预缓存多远?如果您有500个项目,我真的怀疑它会缓存那么远(否则会浪费大量的性能)。当它实际上开始缓存时(即使在480个元素时),这意味着无论如何都要加载另一个批处理。
– Alex Orlov
15年1月27日在18:24
除非将其平滑滚动到很远的位置,否则它不会预缓存内容。在这种情况下,LLM(默认情况下)将布局分为两页而不是一页,以便它可以找到目标视图并减速到该视图。它确实缓存了超出范围的视图。默认情况下,此缓存大小为2,可以通过setItemCacheSize进行配置。
– Yigit
15年1月28日在6:42
@yigit只是在思考问题,我看不到缓存如何在这里带来问题;无论如何,我们只对第一次绑定“最后一个”项目感兴趣!当我们感兴趣的是在列表末尾分页更多数据时,当布局管理器重新使用已绑定的缓存视图时,缺少对onBindViewHolder的调用就不是问题。正确?
–格雷格·恩尼斯(Greg Ennis)
15/12/22在0:36
在我的FlexibleAdapter库中,我选择在onBindViewHolder()中的调用中采用无限滚动功能,它的工作方式就像一个魅力。与滚动侦听器相比,该实现很小,并且更易于使用。
–Davideas
16年7月25日在9:47
#13 楼
我检测加载事件的方法不是检测滚动,而是侦听是否附加了最后一个视图。如果附加了最后一个视图,我认为这是加载更多内容的时机。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) {}
}
#14 楼
我会尝试扩展使用的LayoutManager
(例如LinearLayoutManager
)并覆盖scrollVerticallyBy()
方法。首先,我将首先调用super
,然后检查返回的整数值。如果该值等于0
,则到达底部或顶部边界。然后,我将使用findLastVisibleItemPosition()
方法找出到达哪个边界,并在需要时加载更多数据。只是一个想法。此外,您甚至可以从该方法返回值,从而允许过度滚动并显示“正在加载”指示器。
#15 楼
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
}
}
}
});
评论
onScrollStateChanged()将不起作用,因为只有在更改滚动状态时才会调用它,而在滚动时不会调用。您必须使用onScrolled()方法。
–mradzinski
2015年1月20日在23:56
#16 楼
我在onBindViewHolder
类的RecyclerView.Adapter
方法中使用此逻辑实现了无限滚动类型的实现。 if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) {
if (mCurrentPage == mTotalPageCount) {
mLoadImagesListener.noMorePages();
} else {
int newPage = mCurrentPage + 1;
mLoadImagesListener.loadPage(newPage);
}
}
使用此代码,当RecyclerView到达最后一项时,它将增加当前页面和接口上的回调,该接口负责从api和api加载更多数据。将新结果添加到适配器。
如果不清楚,我可以发布更完整的示例吗?
评论
我知道这篇文章确实很老,但是我有点像个新手,也不知道在哪里可以获取要比较的变量。我假设mCurrentPage和mTotalPageCount是指您的数据集,而mItems是列表项的arraylist,但是如何找到位置?
– BooleanCheese
2015年1月17日在22:37
您可以在这里查看我如何实现RecyclerView.Adapter:github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app / ...
– Speedynomads
15年1月19日在10:40
#17 楼
对于使用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;
}
}
评论
@SudheeshMohan这是完整的类,很小)在我的项目中,我动态更改跨度计数,它将适用于1和2跨度计数
–丹尼斯·津科夫斯基(Dennis Zinkovski)
2015年7月23日在12:16
recyclerView.canScrollVertically(1)可以做到,但是需要API14。:(
– Sudheesh Mohan
15年7月24日在4:55
#18 楼
有一个易于使用的库,名为paginate。同时支持ListView和RecyclerView(LinearLayout,GridLayout和StaggeredGridLayout)。这是Github上项目的链接。
#19 楼
创建一个抽象类并扩展RecyclerView.OnScrollListener
在活动(或片段)中将addOnScrollListener添加到recyclerView
/>
public abstract class EndlessRecyclerOnScrollListener extends RecyclerView.OnScrollListener {
private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold;
private int firstVisibleItem, visibleItemCount, totalItemCount;
private RecyclerView.LayoutManager layoutManager;
public EndlessRecyclerOnScrollListener(RecyclerView.LayoutManager layoutManager, int visibleThreshold) {
this.layoutManager = layoutManager; this.visibleThreshold = visibleThreshold;
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
visibleItemCount = recyclerView.getChildCount();
totalItemCount = layoutManager.getItemCount();
firstVisibleItem = ((LinearLayoutManager)layoutManager).findFirstVisibleItemPosition();
if (loading) {
if (totalItemCount > previousTotal) {
loading = false;
previousTotal = totalItemCount;
}
}
if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
onLoadMore();
loading = true;
}
}
public abstract void onLoadMore();}
#20 楼
我有一个非常详细的示例,说明如何使用RecyclerView进行分页。在较高的级别上,我有一个PAGE_SIZE集合,比方说30。所以我请求30个项目,如果返回30个项目,则请求下一页。如果少于30个项目,则标记一个变量以指示已到达最后一页,然后停止请求更多页面。检查一下,让我知道您的想法。https://medium.com/@etiennelawlor/pagination-with-recyclerview-1cb7e66a502b
#21 楼
在这里,我在Web上使用AsyncListUtil的解决方案说:请注意,此类使用单个线程加载数据,因此适合从辅助存储(例如磁盘)而非网络加载数据。
但是我正在使用odata读取数据并正常工作。
我错过了示例数据实体和网络方法。 >
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();
}
});
}
}
}
#22 楼
@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;
}
}
}
}
评论
我的onScroller()对单个项目调用了三次,所以我得到了end_of_list三次,而我的分页逻辑称为三次。如何更正此问题仅称为分页。
– Ved
16年8月25日在6:34
@ved这不应该发生。上面的代码确保end_of_list条件仅被调用一次。标志_isLastItem是活动中的全局变量,其默认值为false。我要做的是在检测到列表结尾之后执行后台任务,检索下一页,然后通知适配器新数据集,最后将_isLastItem再次设置为false。
– leonapse
16 Sep 2'在8:15
#23 楼
尝试以下操作: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);
}
#24 楼
我使用Abdulaziz Noor答案创建了LoadMoreRecyclerView
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");
}
}
}
//以xml格式添加它
<your.package.LoadMoreRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</your.package.LoadMoreRecyclerView>
OnCreate或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();
}
}
#25 楼
也可以不使用滚动侦听器而仅使用数据模型的纯逻辑来实现。滚动视图需要按位置获取项目以及最大项目数。该模型可以具有后台逻辑,以便以块的形式而不是一个接一个地获取所需的项,然后在后台线程中执行此操作,并在数据准备就绪时通知视图。这种方法允许拥有一个获取队列,该队列优先使用最新请求的(因此当前可见的)项目,而不是较旧的(很可能已经滚动了)的提交,控制要使用的并行线程的数量等。此处提供了此方法的完整源代码(演示应用程序和可重用库)。
#26 楼
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29中有一种方法public void setOnScrollListener (RecyclerView.OnScrollListener listener)
。使用那个编辑:
覆盖
onScrollStateChanged
内的onScrollListener
方法,并执行此操作 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
}
评论
请更具体!如何使用RecyclerView.OnScrollListener确定用户滚动到列表的末尾?
–erdna
14-10-24在7:48
RecyclerView.OnScrollListener上没有onScroll!您将AbsListView.OnScrollListener与RecyclerView.OnScrollListener混合。
–erdna
2014-10-24 8:10
你应该是onScrollStateChanged
–豹
14-10-24在8:17
onScrollStateChanged将不起作用,因为它将仅在更改滚动状态时(而不是在滚动状态时)被调用。
–佩德罗·奥利维拉(Pedro Oliveira)
14-10-24在8:20
@PedroOliveira是否可以通过onScrollStateChanged确定用户滚动到最后一项?
–erdna
2014-10-24 8:49
#27 楼
检查这一切是否详细解释:使用RecyclerView从A到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();
}
在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);
}
}
评论
该链接可能包含答案,但请尝试在您的帖子的链接中解释其中的大部分解释。
–伊恩
16-2-23在23:52
#28 楼
如果列表太小或太小,这些答案都不会考虑在内。这是我一直在使用的一段代码,可双向使用在RecycleViews上。
@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();
}
}
});
}
#29 楼
我让你近似一下。对我来说效果很好。希望对您有所帮助。
/**
* 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);
}
#30 楼
此解决方案非常适合我。//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);
评论
请检查此链接,它将为您提供帮助。.stackoverflow.com/questions/26293461/…请仔细阅读问题!我知道如何使用ListView做到这一点,但不知道如何使用RecycleView来实现它。
如何在android.data中实现loadmore onscrolllistener,但已加载但已更新现有数据,请帮助我
您可以使用以下库:github.com/rockerhieu/rv-adapter-endless。它基于ListView的cwac-endless的思想。
github.com/MarkoMilos/Paginate