How to implement endless list with RecyclerView?

前端 未结 30 2543
无人及你
无人及你 2020-11-22 02:22

I would like to change ListView to RecyclerView. I want to use the onScroll of the OnScrollListener in RecyclerView to determine if a

相关标签:
30条回答
  • 2020-11-22 02:54

    For me, it's very simple:

         private boolean mLoading = false;
    
         mList.setOnScrollListener(new RecyclerView.OnScrollListener() {
    
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
    
                int totalItem = mLinearLayoutManager.getItemCount();
                int lastVisibleItem = mLinearLayoutManager.findLastVisibleItemPosition();
    
                if (!mLoading && lastVisibleItem == totalItem - 1) {
                    mLoading = true;
                    // Scrolled to bottom. Do something here.
                    mLoading = false;
                }
            }
        });
    

    Be careful with asynchronous jobs: mLoading must be changed at the end of the asynchronous jobs. Hope it will be helpful!

    0 讨论(0)
  • 2020-11-22 02:57

    Thanks to @Kushal and this is how I implemented it

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

    Don't forget to add

    LinearLayoutManager mLayoutManager;
    mLayoutManager = new LinearLayoutManager(this);
    mRecyclerView.setLayoutManager(mLayoutManager);
    
    0 讨论(0)
  • 2020-11-22 02:57
    1. Create an abstract class and extends 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. in activity (or fragment) add addOnScrollListener to recyclerView

      LinearLayoutManager mLayoutManager = new LinearLayoutManager(this);
      recyclerView.setLayoutManager(mLayoutManager);
      recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager, 3) {
          @Override
          public void onLoadMore() {
              //TODO
              ...
          }
      });
      
    0 讨论(0)
  • 2020-11-22 03:00

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

    And then use it like this:

    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:

    1. It uses onScrollStateChanged instead of onScroll. This is better because onScroll is called every time there is any sort of movement in the RecyclerView, whereas onScrollStateChanged is only called when the state of the RecyclerView is changed. Using onScrollStateChanged will save you CPU time and, as a consequence, battery.
    2. Since this uses Extension Functions, this can be used in any RecyclerView you have. The client code is just 1 line.
    0 讨论(0)
  • 2020-11-22 03:00

    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.

    0 讨论(0)
  • 2020-11-22 03:01

    For people who use StaggeredGridLayoutManager here is my implementation, it works for me.

     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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题