Làm thế nào để thực hiện danh sách vô tận với RecyclerView?


346

Tôi muốn đổi ListViewsang RecyclerView. Tôi muốn sử dụng onScrollcủaOnScrollListener trong RecyclerViewđể xác định xem một người dùng cuộn đến cuối danh sách.

Làm cách nào để biết người dùng cuộn đến cuối danh sách để tôi có thể tìm nạp dữ liệu mới từ dịch vụ REST?


Vui lòng kiểm tra liên kết này Nó sẽ giúp bạn .. stackoverflow.com/questions/26293461/ trên
samsad

32
Xin vui lòng đọc câu hỏi cẩn thận! Tôi biết cách thực hiện việc này với ListView nhưng KHÔNG làm thế nào để triển khai nó với RecyclView .
erdna

Cách triển khai loadmore onscrolllistener trong android.data được tải nhưng được cập nhật trên dữ liệu hiện có, hãy giúp tôi
Harsha

2
Bạn có thể sử dụng thư viện này: github.com/rockerhieu/rv-ad CHƯƠNG-endless . Nó dựa trên ý tưởng cwac-endlesscho ListView.
Hiếu Rocker

Câu trả lời:


422

Cảm ơn @Kushal và đây là cách tôi triển khai nó

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

Đừng quên thêm

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

11
Tôi có thể làm gì choStaggeredGridLayoutManager
Pratik Butani

16
Thế cònmLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
laaptu

45
Đối với tất cả những người findFirstVisibleItemPosition()không giải quyết, bạn nên đổi RecyclerView.LayoutManagerthànhLinearLayoutManager
Amr Mohammed

26
Đâu là điều kiện để làm loading = truelại?
Bhargav

10
Đừng quên thêm loading = truesau //Do paginationphần. nếu không, mã này sẽ chỉ chạy một lần và bạn không thể tải thêm mục.
Shaishav Jogani

165

Thực hiện các biến này.

private int previousTotal = 0;
private boolean loading = true;
private int visibleThreshold = 5;
int firstVisibleItem, visibleItemCount, totalItemCount;

Đặt trên Scroll để xem chế độ tái chế.

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

Lưu ý: Đảm bảo bạn đang sử dụng LinearLayoutManagerlàm trình quản lý bố cục cho RecyclerView.

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

và cho một lưới

GridLayoutManager mLayoutManager;
mLayoutManager = new GridLayoutManager(getActivity(), spanCount);
mRecyclerView.setLayoutManager(mLayoutManager);

Hãy vui vẻ với cuộn bất tận của bạn !! ^. ^

Cập nhật: mRecyclerView. setOnScrollListener () không dùng nữa chỉ cần thay thế mRecyclerView.addOnScrollListener()và cảnh báo sẽ biến mất! Bạn có thể đọc thêm từ câu hỏi SO này .

Vì Android hiện chính thức hỗ trợ Kotlin, đây là bản cập nhật cho cùng -

Tạo 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
        }
    }
}

và thêm nó vào RecyclerView của bạn như thế này

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

Đối với một ví dụ mã đầy đủ, vui lòng tham khảo repo Github này .


14
Bạn có biết cách áp dụng giải pháp này với GridLayoutManager không? Vì findFirstVisibleItemPosition()phương thức này chỉ khả dụng với linearLayoutManager, tôi không thể tìm ra cách làm cho nó hoạt động.
MathieuMaree

1
Thông tin bổ sung: trong trường hợp này, sử dụng visibleItemCount = mLayoutManager.getChildCount();hành vi giống như sử dụng visibleItemCount = mRecyclerView.getChildCount();.
Jing Li

3
Đặt mã trong phương thức onScrollStateChanged () thay vì phương thức onScrolled ().
Anirudh

2
@ toobsco42 Bạn có thể sử dụng cái này:int[] firstVisibleItemPositions = new int[yourNumberOfColumns]; pastVisiblesItems = layoutManager.findFirstVisibleItemPositions(firstVisibleItemPositions)[0];
MathieuMaree 27/2/2015

1
Điều này findFirstVisibleItemPocation () chưa được giải quyết
Suresh Sharma

72

Đối với những người chỉ muốn nhận thông báo khi mục cuối cùng được hiển thị hoàn toàn, bạn có thể sử dụng View.canScrollVertically().

Đây là cách thực hiện của tôi:

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

Lưu ý: Bạn có thể sử dụng recyclerView.getLayoutManager().canScrollVertically()nếu bạn muốn hỗ trợ API <14.


Đây là một câu trả lời tuyệt vời và đơn giản! Cảm ơn bạn!
Andranik

2
Một giải pháp tương tự có thể được sử dụng để kéo để làm mới bằng cách kiểm tra! RecyclerView.canScrollVertally (-1).
LordParsley

Giải pháp tốt nhất hiện có. Cảm ơn anh bạn!
Angmar

1
@LordParsley Cảm ơn bạn, đã cập nhật. Đây cũng là một trong những cách SwipeRefreshLayout kiểm tra kéo để làm mới.
Hai Zhang

1
@EpicPandaForce Nếu bạn đang sử dụng, RecyclerViewbạn sẽ lại gặp may mắn, bởi vì bạn có thể chỉ cần tạo một bản sao tĩnh View.canScrollVertically()vì các phương thức liên quan được đánh dấu công khai thay vì được bảo vệ RecyclerView. Bạn cũng có thể mở rộng RecyclerViewđể thực hiện lại canScrollVertically()nếu bạn muốn. Cách thứ ba và đơn giản là gọi recyclerView.getLayoutManager().canScrollVertically(). Chỉnh sửa trong câu trả lời của tôi.
Hai Zhang

67

Đây là một cách tiếp cận khác. Nó sẽ làm việc với bất kỳ trình quản lý bố trí.

  1. Làm cho lớp Adaptor trừu tượng
  2. Sau đó, tạo một phương thức trừu tượng trong lớp bộ điều hợp (ví dụ: load ())
  3. Trong onBindViewHolder kiểm tra vị trí nếu cuối cùng và gọi tải ()
  4. Ghi đè hàm load () trong khi tạo đối tượng bộ điều hợp trong hoạt động hoặc đoạn của bạn.
  5. Trong chức năng tải chồng chéo thực hiện cuộc gọi loadmore của bạn

Để hiểu chi tiết, tôi đã viết một bài đăng trên blog và dự án ví dụ lấy nó ở đây http://sab99r.com/blog/recyclerview-endless-load-more/

MyAd CHƯƠNG.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
Giống như giải pháp, bạn không cần phải làm cho bộ điều hợp của mình trừu tượng, thay vào đó bạn có thể tạo giao diện trong bộ điều hợp và thực hiện hoạt động của mình để giữ cho nó linh hoạt và thanh lịch, ví dụ: ItemDisplayListener => onItemDisplayed (int vị trí). Bằng cách này, bạn có thể tải ở bất kỳ vị trí nào: tại N, N-1, N-2 ..
Samer

1
@mcwise thêm boolean xong = false; khi bạn kết thúc, hãy kết thúc = true .. thay đổi điều kiện if hiện tại thành if ((vị trí> = getItemCount () - 1) && (! đã hoàn thành))
Sabeer Mohammed

1
Điều này nên được đánh dấu là câu trả lời chính xác. Đây là thanh lịch và đơn giản.
Greg Enni

2
Một ví dụ điển hình về giải pháp ưu việt bị chôn vùi dưới sự phổ biến của giải pháp đầu tiên. Rất thường xuyên, hàng cuối cùng sẽ được gắn vào một chế độ xem riêng biệt dùng làm biểu ngữ "Đang tải ...". Khi chế độ xem này nhận được một cuộc gọi liên kết, nó sẽ thiết lập một cuộc gọi lại để kích hoạt thông báo thay đổi dữ liệu khi có nhiều dữ liệu hơn. Câu trả lời được chấp nhận của việc kết nối trình nghe cuộn là lãng phí và có thể không hoạt động đối với các kiểu bố cục khác.
Michael Hays

3
@mcwise nó sẽ chỉ gọi vô tận nếu người dùng chủ động cuộn đi và quay lại mục cuối cùng. onBindViewHolder chỉ được gọi một lần mỗi lần khung nhìn vào màn hình và sẽ không liên tục gọi nó.
David Liu

18

Câu trả lời của tôi là một phiên bản sửa đổi của Noor. Tôi đã chuyển từ một ListViewnơi tôi đã có EndlessScrollListener(mà bạn có thể dễ dàng tìm thấy trong nhiều câu trả lời trên SO) đến một RecyclerViewvì vậy tôi muốn một EndlessRecyclScrollListenerbản cập nhật dễ dàng cho người nghe trước đây của tôi.

Vì vậy, đây là mã, hy vọng nó sẽ giúp:

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
Việc triển khai của bạn yêu cầu RecyclerView sử dụng linearLeayoutManager. Ý tưởng tồi!
Orri

@Orri Bạn có thể giải thích tại sao? Cảm ơn!
Federico Ponzi

1
Trong onScrolled của bạn, bạn đã sử dụng Trình quản lý bố cục từ RecyclerView sang linearLayoutManager. Nó không hoạt động cho StaggredGridLayout hoặc chống lại người khác. Không có findFirstVisibleItemP vị trí () trong StaggredGridLayout.
Orri

@Orri Cảm ơn bạn đã chỉ ra điều đó! Nhưng tôi không nghĩ đó là một ý tưởng tồi, vì nó hiệu quả với tôi và, dựa trên những người ủng hộ, các trường hợp khác :)
Federico Ponzi

10

Đối với tôi, nó rất đơn giản:

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

Hãy cẩn thận với các công việc không đồng bộ: tải thay đổi phải được thay đổi vào cuối các công việc không đồng bộ. Hy vọng nó sẽ hữu ích!


Về cơ bản, tôi đã tìm thấy câu trả lời này sau nhiều ngày tự hỏi. Đây là giải pháp tốt nhất, imo.
wsgeorge

vui lòng cung cấp một số giải pháp cho recyclerview với số trang loadmore qua Android
Harsha

getWebServiceData (); mStoresAd ModuleData.setOnLoadMoreListener (cửa hàng mớiAdAd.OnLoadMoreListener () {@Override công khai void onLoadMore () {// thêm null, vì vậy bộ điều hợp sẽ kiểm tra view_type và hiển thị thanh tiến trình ở cửa hàng dưới cùng () - 1); ++ trangSố; getWebServiceData ();}}); cả hai lần lượt gọi nhau dữ liệu trang thứ 2 chỉ hiển thị xin vui lòng giúp đỡ
Harsha

tôi đã thử thực hiện phân trang recyclerView nhưng không ai trong số họ làm việc, ngoại trừ cái này, bạn có ông chủ của tôi
Mohammed Elrashied

9

Hầu hết các câu trả lời đều giả sử việc RecyclerViewsử dụng a LinearLayoutManager, hoặc GridLayoutManager, hoặc thậm chí StaggeredGridLayoutManager, hoặc giả sử rằng cuộn là dọc hoặc horyzontal, nhưng không ai đã đăng một câu trả lời hoàn toàn chung chung .

Sử dụng ViewHolderbộ chuyển đổi rõ ràng không phải là một giải pháp tốt. Một bộ chuyển đổi có thể có nhiều hơn 1 RecyclerViewsử dụng nó. Nó "thích nghi" nội dung của họ. Nó phải là Chế độ xem tái chế (là một lớp chịu trách nhiệm về những gì hiện đang hiển thị cho người dùng và không phải là bộ điều hợp chỉ chịu trách nhiệm cung cấp nội dung cho RecyclerView) mà phải thông báo cho hệ thống của bạn rằng cần thêm vật phẩm (để tải).

Đây là giải pháp của tôi, không sử dụng gì khác ngoài các lớp trừu tượng của RecyclerView (RecycerView.LayoutManager và RecycerView.Ad CHƯƠNG):

/**
 * 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
Câu trả lời của bạn là đúng và nó hoạt động cho tất cả LayoutManagersnhưng chỉ một khoảnh khắc: di chuyển mã tính toán của bạn sang một phương thức scrollListener khác - thay thế nó để onScrollStateChangedthay thế onScrolled. Và onScrollStateChangedchỉ cần kiểm tra:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer

Cảm ơn lời khuyên của bạn, nhưng tôi không nghĩ nó sẽ là một giải pháp tốt hơn. Nhà phát triển muốn trình nghe của nó được kích hoạt ngay cả trước khi recyclerview dừng để cuộn. Vì vậy, nó có thể kết thúc để tải các mục dữ liệu mới (nếu dữ liệu được lưu trữ cục bộ) hoặc thậm chí thêm một trình giữ khung nhìn thanh tiến trình ở cuối recyclerview, do đó, khi tái chế sẽ dừng để cuộn, nó có thể nằm trong tiến trình vòng tròn -bar là mục hiển thị cuối cùng.
Yairopro

6

Mặc dù câu trả lời được chấp nhận hoạt động hoàn hảo, nhưng giải pháp bên dưới sử dụng addOnScrollListener vì setOnScrollListener không được dùng nữa và giảm số lượng biến và nếu có điều kiện.

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
Đôi khi phương thức loadMore () của tôi được gọi là ba lần trong onScrolled (), Bất kỳ ý tưởng nào tại sao nó được gọi là ba lần và làm thế nào để ngừng gọi nó nhiều lần.
ved

dy> 0 luôn sai
Dushyant Suthar

@ved Đó là vì cuộn có 3 trạng thái: cuộn, cuộn, không hoạt động. Bạn chọn một cái phù hợp.
TheRealChx101

5

Mặc dù có rất nhiều câu trả lời cho câu hỏi, tôi muốn chia sẻ kinh nghiệm của chúng tôi về việc tạo chế độ xem danh sách vô tận. Gần đây chúng tôi đã triển khai Trình quản lý Carousel tùy chỉnh có thể hoạt động theo chu kỳ bằng cách cuộn danh sách vô hạn cũng như đến một điểm nhất định. Dưới đây là một mô tả chi tiết trên GitHub .

Tôi khuyên bạn nên xem bài viết này với các đề xuất ngắn nhưng có giá trị về cách tạo Bố cục tùy chỉnh: http://case.azoft.com/create-custom-layoutmanager-android/


5

Với sức mạnh của các chức năng mở rộng của Kotlin, mã có thể trông thanh lịch hơn rất nhiều. Đặt cái này ở bất cứ đâu bạn muốn (tôi có nó trong tệp ExtensionFiances.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
                }
            }
        }
    })
}

Và sau đó sử dụng nó như thế này:

youRecyclerView.addOnScrolledToEnd {
    //What you want to do once the end is reached
}

Giải pháp này dựa trên câu trả lời của Kushal Sharma. Tuy nhiên, điều này tốt hơn một chút vì:

  1. Nó sử dụng onScrollStateChangedthay vì onScroll. Điều này tốt hơn bởi vì onScrollđược gọi mỗi khi có bất kỳ loại chuyển động nào trong RecyclerView, trong khi onScrollStateChangedchỉ được gọi khi trạng thái của RecyclerView bị thay đổi. Sử dụng onScrollStateChangedsẽ giúp bạn tiết kiệm thời gian CPU và do đó, pin.
  2. Vì điều này sử dụng Hàm mở rộng, nên điều này có thể được sử dụng trong bất kỳ RecyclerView nào bạn có. Mã khách hàng chỉ là 1 dòng.

Khi tôi thực hiện Pull SwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working lớp riêng bên trong PullToRefresh: SwipeRefreshLayout.OnRefreshListener {override fun onRefresh () {pbViewMore !!. Visual = View.GONE pageCount = 0 CourseList.clear () // gọi asynctrrrrrrrrrrrr ! .isRefreshing = false}}
Naveen Kumar M

5

Đây là cách tôi làm, đơn giản và ngắn gọn:

    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

OK, tôi đã làm điều đó bằng cách sử dụng onBindViewHolder phương thức của RecyclerView.Ad .

Bộ chuyển đổi:

public interface OnViewHolderListener {
    void onRequestedLastItem();
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    ...

    if (position == getItemCount() - 1) onViewHolderListener.onRequestedLastItem();
}

Đoạn (hoặc Hoạt động) :

@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
dựa vào onBind không phải là một ý kiến ​​hay RecyclerView thực hiện các chế độ xem bộ đệm + có một số trình quản lý bố cục có nghĩa là lưu trữ trước các thứ. Tôi sẽ đề nghị thiết lập trình nghe cuộn và khi trình nghe cuộn dừng, hãy lấy vị trí mục hiển thị cuối cùng từ trình quản lý bố cục. Nếu bạn đang sử dụng trình quản lý bố cục mặc định, tất cả chúng đều có các phương thức findLastVisible **.
số

@yigit RecyclerView sẽ cố gắng đi xa đến đâu? Nếu bạn có 500 mục, tôi thực sự nghi ngờ nó sẽ lưu trữ quá xa (nếu không nó sẽ chỉ là một sự lãng phí hiệu năng khổng lồ). Khi nó thực sự sẽ bắt đầu lưu vào bộ đệm (ngay cả khi chúng tôi ở mức 480 phần tử), điều đó có nghĩa là đã đến lúc tải một lô khác.
Alex Orlov

1
Nó không snot những thứ preCache trừ khi nó di chuyển trơn tru đến một vị trí xa. Trong trường hợp đó, LLM (theo mặc định) bố trí hai trang thay vì một trang để nó có thể tìm thấy chế độ xem mục tiêu và giảm tốc độ cho nó. Nó không xem các bộ nhớ cache đi ra ngoài giới hạn. Theo mặc định, kích thước bộ đệm này là 2 và có thể được cấu hình thông qua setItemCacheSize.
kỹ thuật số

1
@yigit chỉ nghĩ về vấn đề Tôi không thấy làm thế nào bộ nhớ đệm có thể trình bày một vấn đề ở đây; chúng tôi chỉ quan tâm đến lần đầu tiên các mục "cuối cùng" bị ràng buộc nào! Thiếu cuộc gọi đến onBindViewHolder khi trình quản lý bố cục sử dụng lại chế độ xem được lưu trong bộ nhớ cache đã bị ràng buộc không phải là vấn đề khi chúng ta thú vị trong việc phân trang nhiều dữ liệu ở cuối danh sách. Chính xác?
Greg Enni

2
Trong thư viện Linh hoạt của tôi, tôi đã chọn áp dụng cuộn vô tận với lệnh gọi onBindViewHolder(), nó hoạt động như một bùa mê. Việc thực hiện rất nhỏ so với trình nghe cuộn và nó dễ sử dụng hơn.
Davideas

3

Cách của tôi để phát hiện sự kiện tải không phải là phát hiện cuộn, mà là lắng nghe xem lần xem cuối có được đính kèm hay không. Nếu chế độ xem cuối cùng được đính kèm, tôi coi đó là thời gian để tải thêm nội dung.

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

Tôi sẽ cố gắng mở rộng phương thức được sử dụng LayoutManager(ví dụ LinearLayoutManager) và ghi đè scrollVerticallyBy(). Đầu tiên, tôi sẽ gọi supertrước và sau đó kiểm tra giá trị nguyên được trả về. Nếu giá trị bằng với 0thì chạm đáy hoặc viền trên cùng. Sau đó tôi sẽ sử dụngfindLastVisibleItemPosition() phương pháp để tìm ra đường viền nào đạt được và tải thêm dữ liệu nếu cần. Chỉ là một ý tưởng.

Ngoài ra, bạn thậm chí có thể trả về giá trị của mình từ phương thức đó cho phép ghi đè và hiển thị chỉ báo "đang tải".


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()sẽ không hoạt động vì nó sẽ chỉ được gọi khi bạn thay đổi trạng thái của cuộn chứ không phải trong khi bạn cuộn. Bạn phải sử dụng onScrolled()phương pháp.
mradzinski

2

Tôi đã đạt được một triển khai kiểu cuộn vô hạn bằng cách sử dụng logic này trong onBindViewHolderphương thức của RecyclerView.Adapterlớp.

    if (position == mItems.size() - 1 && mCurrentPage <= mTotalPageCount) {
        if (mCurrentPage == mTotalPageCount) {
            mLoadImagesListener.noMorePages();
        } else {
            int newPage = mCurrentPage + 1;
            mLoadImagesListener.loadPage(newPage);
        }
    }

Với mã này khi RecyclerView đến mục cuối cùng, nó sẽ tăng trang hiện tại và các cuộc gọi lại trên một giao diện chịu trách nhiệm tải thêm dữ liệu từ api và thêm kết quả mới vào bộ điều hợp.

Tôi có thể đăng ví dụ đầy đủ hơn nếu điều này không rõ ràng?


Tôi biết bài đăng này thực sự cũ, nhưng tôi phần nào là người mới và không biết nơi nào để lấy các biến bạn đang so sánh. mCienPage và mTotalPageCount đang tham chiếu đến tập dữ liệu của bạn mà tôi giả sử và mItems là danh sách mảng của các mục danh sách, nhưng làm cách nào để tìm vị trí?
BooleanCheese 17/1/2015

Bạn có thể thấy cách tôi triển khai RecyclerView.Ad
CHƯƠNG

2

Đối với những người sử dụng StaggeredGridLayoutManager ở đây là triển khai của tôi, nó hoạt động với tôi.

 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 Đây là lớp đầy đủ, nó nhỏ) Trong dự án của tôi, tôi thay đổi số lượng nhịp động và nó sẽ hoạt động cho số đếm 1 và 2 nhịp
Dennis Zinkovski

recyclerView.canScrollVertively (1) sẽ làm điều đó nhưng nó yêu cầu API 14. :(
Sudheesh Mohan

2

Có một thư viện dễ sử dụng cho phân trang có tên này . Hỗ trợ cả ListView và RecyclerView (linearLayout, GridLayout và StaggeredGridLayout).

Đây là đường dẫn đến dự án trên Github


2
  1. Tạo một lớp trừu tượng và mở rộng 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. trong hoạt động (hoặc đoạn) thêm addOnScrollListener vào recyclerView

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

1

Tôi có một ví dụ khá chi tiết về cách phân trang với RecyclerView. Ở cấp độ cao, tôi có một bộ PAGE_SIZE, giả sử là 30. Vì vậy, tôi yêu cầu 30 mục và nếu tôi nhận được 30 mục thì tôi yêu cầu trang tiếp theo. Nếu tôi nhận được ít hơn 30 mục, tôi gắn cờ một biến để chỉ ra rằng trang cuối cùng đã đạt được và sau đó tôi ngừng yêu cầu thêm trang. Kiểm tra nó và cho tôi biết những gì bạn nghĩ.

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


1

Ở đây, giải pháp của tôi sử dụng AsyncListUtil , trong web nói: Lưu ý rằng lớp này sử dụng một luồng duy nhất để tải dữ liệu, vì vậy nó phù hợp để tải dữ liệu từ bộ lưu trữ thứ cấp như đĩa, nhưng không phải từ mạng. nhưng tôi đang sử dụng odata để đọc dữ liệu và hoạt động tốt. Tôi bỏ lỡ trong các ví dụ dữ liệu thực thể và phương thức mạng của tôi. Tôi chỉ bao gồm các bộ chuyển đổi ví dụ.

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

Tại sao không sử dụng logic này thay thế?

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 của tôi () được gọi là ba lần cho một mục duy nhất để tôi nhận được ba lần end_of_list và logic phân trang của tôi được gọi là ba lần. Làm thế nào để sửa lỗi này để gọi phân trang chỉ một lần.
ved

@ved Điều đó không được phép xảy ra. Mã ở trên đảm bảo end_of_listđiều kiện sẽ chỉ được gọi một lần. Cờ _isLastItemlà một biến toàn cục trong hoạt động có giá trị mặc định là false. Những gì tôi đã làm là thực hiện một tác vụ nền sau khi phát hiện phần cuối của danh sách, lấy trang tiếp theo sau đó thông báo cho bộ điều hợp của tập dữ liệu mới và cuối cùng đặt _isLastItemlại thành sai.
leonapse

1

Hãy thử dưới đây:

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

Tôi đã tạo LoadMoreRecyclerView bằng Abdulaziz Noor Trả lời

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

// Thêm nó vào xml như

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

OnCreate hoặc onViewCreated

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

dịch vụ cuộc gọi của bạn

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

Cũng có thể thực hiện mà không cần người nghe cuộn, chỉ sử dụng logic thuần túy của mô hình dữ liệu. Chế độ xem cuộn yêu cầu nhận các mục theo vị trí cũng như số lượng mục tối đa. Mô hình có thể có logic nền để tìm nạp các mục cần thiết theo từng khối, thay vì từng cái một và thực hiện điều này trong luồng nền, thông báo chế độ xem khi dữ liệu đã sẵn sàng.

Cách tiếp cận này cho phép có hàng đợi tìm nạp thích các mục được yêu cầu gần đây nhất (hiện đang hiển thị) so với các bài nộp cũ hơn (rất có thể đã được cuộn đi), kiểm soát số lượng chủ đề song song để sử dụng và những thứ tương tự. Mã nguồn hoàn chỉnh cho phương pháp này (ứng dụng demo và thư viện tái sử dụng) có sẵn ở đây .


0

Có một phương thức public void setOnScrollListener (RecyclerView.OnScrollListener listener)trong https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html#setOnScrollListener%28android.support.v7.widget.RecyclerView.OnScrollListener%29 . Sử dụng nó

BIÊN TẬP:

onScrollStateChangedPhương thức ghi đè bên trong onScrollListenervà làm điều này

            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
Xin hãy cụ thể hơn! Làm cách nào để sử dụng RecyclerView.OnScrollListener để xác định rằng người dùng đã cuộn đến cuối danh sách?
erdna

Không có onScroll tại RecyclerView.OnScrollListener ! Bạn trộn absListView.OnScrollListener với RecyclerView.OnScrollListener.
erdna

ya nó được cho làonScrollStateChanged
Panther

onScrollStateChangedsẽ không hoạt động vì nó sẽ chỉ được gọi khi bạn thay đổi trạng thái của cuộn chứ không phải trong khi bạn cuộn.
Pedro Oliveira

@PedroOliveira Có thể onScrollStateChangedxác định rằng người dùng cuộn đến mục cuối cùng không?
erdna

0

Kiểm tra điều này mọi thứ được giải thích chi tiết: Phân trang bằng RecyclerView Từ A đến 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();
}

trong MyAd CHƯƠNG:

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

Liên kết có thể chứa câu trả lời, nhưng hãy cố gắng giải thích một số phần quan trọng của các giải thích trong liên kết trong bài đăng của bạn.
Ian

0

Không có câu trả lời nào trong số này có tính đến nếu danh sách quá nhỏ hay không.

Đây là một đoạn mã tôi đã sử dụng hoạt động trên RecyclViews theo cả hai hướng.

@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

Tôi cho bạn sự gần đúng của tôi. Hoạt động tốt cho tôi.

Tôi hy vọng nó sẽ giúp bạn.

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

Giải pháp này hoạt động hoàn hảo cho tôi.

//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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.