如何使用RecyclerView实现无尽列表?


345

我想更改ListViewRecyclerView。我想使用onScroll的的OnScrollListenerRecyclerView确定用户滚动到列表的末尾。

我如何知道用户是否滚动到列表的末尾,以便可以从REST服务获取新数据?


请检查该链接它会帮助你.. stackoverflow.com/questions/26293461/...
samsad

32
请仔细阅读问题!我知道如何用ListView做到这一点,但不知道如何用RecycleView实现它。
erdna 2014年

如何在android.data中实现loadmore onscrolllistener,但已对现有数据进行了更新,请帮助我
Harsha 2015年

2
您可以使用以下库:github.com/rockerhieu/rv-adapter-endless。它基于cwac-endlessfor 的想法 ListView
Hieu Rocker

Answers:


422

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

不要忘记添加

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

11
我能做什么StaggeredGridLayoutManager
Pratik Butani 2015年

16
怎么样mLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
laaptu

45
对于所有findFirstVisibleItemPosition()仍未解决的人,您应该更改RecyclerView.LayoutManagerLinearLayoutManager
Amr Mohammed

26
loading = true再做的条件在哪里?
巴尔加夫2015年

10
不要忘了添加loading = true//Do pagination部分。否则,此代码将仅运行一次,并且您无法加载更多项目。
Shaishav Jogani

165

进行这些变量。

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存储库


14
是否知道如何通过GridLayoutManager应用此解决方案?由于该findFirstVisibleItemPosition()方法仅适用于LinearLayoutManager,因此我无法弄清楚如何使其工作。
MathieuMaree 2014年

1
其他信息:在这种情况下,using的visibleItemCount = mLayoutManager.getChildCount();行为与using相同visibleItemCount = mRecyclerView.getChildCount();
李静

3
将代码放在onScrollStateChanged()方法而不是onScrolled()方法中。
Anirudh 2014年

2
@ toobsco42您可以使用:int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
MathieuMaree

1
这个findFirstVisibleItemPosition()是什么未解决
Suresh Sharma

72

对于只希望在完全显示最后一项时得到通知的人,可以使用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() {}
}

注意:recyclerView.getLayoutManager().canScrollVertically()如果要支持API <14,则可以使用。


这是一个非常简单的答案!谢谢!
Andranik

2
通过检查!recyclerView.canScrollVertically(-1),可以使用类似的解决方案进行刷新。
LordParsley

最好的解决方案。多谢,伙计!
Angmar

1
@LordParsley谢谢,更新。这也是SwipeRefreshLayout检查拉动刷新的方式之一。
张海

1
@EpicPandaForce如果您使用的RecyclerView话,将会再次感到幸运,因为您可以简单地对其进行静态复制,View.canScrollVertically()因为相关方法被标记为public而不是中的protected RecyclerView。如果愿意,您还可以扩展RecyclerView到重新实现canScrollVertically()。第三种简单方法是致电recyclerView.getLayoutManager().canScrollVertically()。编辑我的答案。
张海

67

这是另一种方法。它将与任何布局管理器一起使用。

  1. 使Adapter类抽象
  2. 然后在适配器类中创建一个抽象方法(例如load())
  3. onBindViewHolder中检查位置是否为last,然后调用load()
  4. 在活动或片段中创建适配器对象时,请覆盖load()函数。
  5. 在overided 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();
            }
        };
   }
}

3
像该解决方案一样,您不必使适配器抽象,而是可以在适配器中创建接口,并使您的活动实现该接口,以使其保持灵活,优雅,例如:ItemDisplayListener => onItemDisplayed(int position)。这样,您可以在任何位置加载:在N,N-1,N-2 ..
Samer

1
@mcwise添加一个布尔完成= false; 当您到达终点时,将finish = true ..将当前的if条件更改为if((position> = getItemCount()-1)&&(!finished))
Sabeer Mohammed 2015年

1
应该将其标记为正确答案。这是优雅而简单的。
格雷格·恩尼斯

2
一个优秀的解决方案很好的例子,它埋在第一个解决方案的流行之下。通常,最后一行将绑定到单独的视图类型,该视图类型用作“正在加载...”横幅。当此视图类型获得绑定调用时,它将设置一个回调,当更多数据可用时,该回调将触发数据更改通知。连接滚动侦听器的公认答案很浪费,并且可能不适用于其他布局类型。
Michael Hays

3
@mcwise仅在用户主动滚动并返回到最后一项时才会无休止地调用。每次视图进入屏幕时,只会调用一次onBindViewHolder,并且不会不断调用它。
David Liu

18

我的答案是Noor的修改版。我从一个ListView我拥有的地方EndlessScrollListener(可以在SO上的许多答案中轻松找到)传递到一个,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);

}

1
您的实现要求RecyclerView使用LinearLeayoutManager。馊主意!
Orri(奥瑞)

@Orri请您解释为什么?谢谢!
Federico Ponzi'7

1
在onScrolled中,将LayoutManager从RecyclerView投射到LinearLayoutManager。它不适用于StaggredGridLayout或其他。StaggredGridLayout中没有findFirstVisibleItemPosition()。
Orri 2016年

@Orri感谢您指出!但是我不认为这是一个坏主意,因为它对我有用,并且根据支持情况,还可以处理其他情况:)
Federico Ponzi

10

对我来说,这很简单:

     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 2015年

请给与loadmore页码传球recyclerview解决一些的android
戒日

getWebServiceData(); mStoresAdapterData.setOnLoadMoreListener(new StoresAdapter.OnLoadMoreListener(){@Override public void onLoadMore(){//添加null,因此适配器将检查view_type并在底部storesList.add(null)处显示进度条; ()-1); ++ pageNumber; getWebServiceData();}}); 两者都一个接一个地调用,最后仅显示第二页数据,请帮助
Harsha

我尝试了许多实现recyclerView分页的方法,但均
无济于事

9

大多数的答案都是假设RecyclerView使用LinearLayoutManager,或者GridLayoutManager,甚至StaggeredGridLayoutManager,或者假设滚动垂直或horyzontal,但没有人发布了一个完全地通用的答案

使用ViewHolder的适配器显然不是一个好的解决方案。一个适配器可能使用了多个适配器RecyclerView。它“适应”它们的内容。应该是Recycler View(这是负责当前显示给用户的类的一类,而不是仅负责向其提供内容的适配器的类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.
    }
}

1
你的答案是正确的,它的工作了所有LayoutManagers,但只有一个时刻:在另一个scrollListener方法将你的计算程序-移动它onScrollStateChanged来代替 onScrolled。而在onScrollStateChanged刚刚检查:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer

感谢您的建议,但我认为这不是更好的解决方案。开发人员希望在recyclerview停止滚动之前就触发其侦听器。因此,它可能会完成加载新数据项(如果数据存储在本地),或者甚至在recyclerview的末尾添加进度条视图持有人,因此当recyclerview停止滚动时,它可能处于循环状态-bar作为最后一个可见项目。
Yairopro

6

尽管可以接受的答案非常完美,但是由于不建议使用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();
            }
        }
    }
});

1
有时,我的loadMore()方法在onScrolled()中称为thrice,不知道为什么它被称为thrice以及如何停止多次调用它。
ved

DY> 0永远是假的
Dushyant Suthar

@ved这是因为滚动具有3种状态:滚动,滚动,空闲。您选择一个合适的。
TheRealChx101 '18


5

借助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的回答。但是,这会更好一点,因为:

  1. 它使用onScrollStateChanged代替onScroll。这样做更好,因为onScroll每次在RecyclerView中发生任何形式的移动时onScrollStateChanged都会调用它,而仅当RecyclerView的状态更改时才调用。使用onScrollStateChanged将节省您的CPU时间,并因此节省电池。
  2. 由于它使用扩展功能,因此可以在您拥有的任何RecyclerView中使用。客户端代码只有1行。

当我完成Pull SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working 内部私有类PullToRefresh时:SwipeRefreshLayout.OnRefreshListener {重写fun onRefresh(){pbViewMore !!。visibility = View.GONE pageCount = 0 courseList.clear()//在此处调用asynctask srlNoMessageRefreshLayout !!。isRefreshing =错误的swipeRefreshLayout! !.isRefreshing = false}}
Naveen Kumar M,

5

这是我的操作方式,简单而简短:

    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

            }
        }
    });

3

好的,我通过使用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;
}

1
依靠onBind并不是一个好主意。RecyclerView确实可以缓存视图+具有一些布局管理器,可以预缓存事物。我建议设置一个滚动侦听器,当滚动侦听器停止时,从布局管理器获取最后一个可见项目的位置。如果使用的是默认布局管理器,则它们都具有findLastVisible **方法。
yigit

@yigit RecyclerView将尝试预缓存多远?如果您有500个项目,我真的怀疑它会缓存那么远(否则会浪费大量的性能)。当它真正开始缓存时(即使在480个元素时),这意味着无论如何都要加载另一个批处理。
Alex Orlov

1
除非它平滑滚动到很远的位置,否则它不会预缓存内容。在这种情况下,LLM(默认情况下)将布局分为两页而不是一页,以便它可以找到目标视图并减速到该视图。它确实缓存了超出范围的视图。默认情况下,此缓存大小为2,可以通过setItemCacheSize进行配置。
yigit

1
@yigit只是在思考问题,我看不到缓存如何在这里带来问题;无论如何,我们只对第一次绑定“最后一个”项目感兴趣!当我们感兴趣的是在列表末尾分页更多数据时,当布局管理器重新使用已绑定的缓存视图时,错过对onBindViewHolder的调用就不是问题。正确?
格雷格·恩尼斯

2
在我的FlexibleAdapter库中,我选择了通过in进行无限滚动onBindViewHolder(),它的工作方式就像一个魅力。与滚动侦听器相比,该实现很小,并且更易于使用。
Davideas

3

我检测加载事件的方法不是检测滚动,而是侦听是否附加了最后一个视图。如果附加了最后一个视图,我认为这是加载更多内容的时机。

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

2

我会尝试扩展使用LayoutManager(例如LinearLayoutManager)和重写scrollVerticallyBy()方法。首先,我super先调用,然后检查返回的整数值。如果该值等于,0则将达到底部或顶部边界。然后,我将使用findLastVisibleItemPosition()方法找出到达哪个边界,并在需要时加载更多数据。只是一个主意。

另外,您甚至可以从该方法返回值,从而允许过度滚动并显示“正在加载”指示器。


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

1
onScrollStateChanged()将不起作用,因为仅当您更改滚动状态时才会调用它,而在滚动时则不会调用它。您必须使用该onScrolled()方法。
mradzinski

2

我在课堂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加载更多数据并将新结果添加到适配器。

如果不清楚,我可以发布更完整的示例吗?


我知道这篇文章确实很老,但是我有点像个新手,也不知道在哪里可以获取要比较的变量。我假设mCurrentPage和mTotalPageCount是指您的数据集,而mItems是列表项的arraylist,但是如何找到位置?
BooleanCheese 2015年

你可以看到我是如何实现我的RecyclerView.Adapter这里:github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app/...
speedynomads

2

对于使用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

recyclerView.canScrollVertically(1)可以做到,但需要API 14::(
Sudheesh Mohan

2

有一个易于使用的库,名为paginate。同时支持ListView和RecyclerView(LinearLayout,GridLayout和StaggeredGridLayout)。

这是Github上项目的链接


2
  1. 创建一个抽象类并扩展RecyclerView.OnScrollListener

    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();}
  2. 在活动(或片段)中将addOnScrollListener添加到recyclerView

    LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager, 3) {
        @Override
        public void onLoadMore() {
            //TODO
            ...
        }
    });

1

我有一个非常详细的示例,说明如何使用RecyclerView进行分页。在较高的级别上,我有一个PAGE_SIZE集合,比方说30。所以我请求30个项目,如果返回30个项目,则请求下一页。如果少于30个项目,则标记一个变量以指示已到达最后一页,然后停止请求更多页面。检查一下,让我知道您的想法。

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


1

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

1

@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这不应该发生。上面的代码确保end_of_list条件只会被调用一次。该标志_isLastItem是活动中的全局变量,其默认值为false。我要做的是在检测到列表结尾后执行后台任务,检索下一页,然后将新数据集通知适配器,最后_isLastItem再次设置为false。
leonapse

1

请尝试以下方法:

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

}

1

我已经使用Abdulaziz Noor Answer创建了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();
    }
}

1

在没有滚动侦听器的情况下,也可以仅使用数据模型的纯逻辑来实现。滚动视图需要按位置获取项目以及最大项目数。该模型可以具有后台逻辑,以块的形式而不是一个接一个地获取所需的项,并在后台线程中执行此操作,并在数据准备好时通知视图。

这种方法允许具有获取队列,该队列优先使用最新请求的(因此当前可见的)项目,而不是较旧的(最有可能已经滚动掉)的提交,控制要使用的并行线程的数量等等。此处提供了此方法的完整源代码(演示应用程序和可重用库)。


0

有一种方法public void setOnScrollListener (RecyclerView.OnScrollListener listener)https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29。用那个

编辑:

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
            }

1
请更具体!如何使用RecyclerView.OnScrollListener确定用户滚动到列表的末尾?
erdna 2014年

RecyclerView.OnScrollListener处没有onScroll !您将AbsListView.OnScrollListener与RecyclerView.OnScrollListener混合。
erdna 2014年

ya应该是onScrollStateChanged
Panther

onScrollStateChanged将不起作用,因为仅当您更改滚动状态时才会调用它,而在滚动时则不会调用它。
Pedro Oliveira 2014年

@PedroOliveira是否onScrollStateChanged可以确定用户滚动到最后一个项目?
erdna 2014年

0

检查一下这一切是否详细解释: 使用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);
    }
}

该链接可能包含答案,但请尝试在您的帖子的链接中解释其中的大部分解释。
伊恩

0

如果列表太小或太小,这些答案都不会考虑在内。

这是我一直在使用的一段代码,可在两个方向上在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();
                }
            }

        });
    }

0

我让你接近我。对我来说很好。

希望对您有帮助。

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

}

0

该解决方案非常适合我。

//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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.