วิธีการใช้รายการที่ไม่สิ้นสุดด้วย RecyclerView


346

ผมอยากจะเปลี่ยนไปListView RecyclerViewฉันต้องการที่จะใช้onScrollของOnScrollListenerในRecyclerViewเพื่อตรวจสอบว่าผู้ใช้เลื่อนไปที่ส่วนท้ายของรายการ

ฉันจะรู้ได้อย่างไรว่าผู้ใช้เลื่อนไปที่ส่วนท้ายของรายการเพื่อให้สามารถดึงข้อมูลใหม่จากบริการ REST ได้หรือไม่


กรุณาตรวจสอบลิงค์นี้มันจะช่วยให้คุณ .. stackoverflow.com/questions/26293461/…
samsad

32
โปรดอ่านคำถามอย่างระมัดระวัง! ฉันรู้วิธีการทำเช่นนี้กับ ListView แต่ไม่ใช่วิธีการที่จะใช้มันกับRecycleView
erdna

วิธีการใช้ loadmore onscrolllistener ใน android.data ถูกโหลด แต่ได้รับการอัปเดตข้อมูลที่มีอยู่โปรดช่วยฉันด้วย
Harsha

2
คุณสามารถใช้ห้องสมุดนี้: github.com/rockerhieu/rv-adapter-endless มันขึ้นอยู่กับความคิดของ สำหรับcwac-endless ListView
Hieu Rocker

คำตอบ:


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

16
แล้วmLayoutManager.findLastCompletelyVisibleItemPosition()==mLayoutManager.getItemCount()-1
laaptu

45
สำหรับทุกคนที่findFirstVisibleItemPosition()ไม่ได้รับการแก้ไขคุณควรเปลี่ยนRecyclerView.LayoutManagerเป็นLinearLayoutManager
Amr Mohammed

26
เงื่อนไขสำหรับการสร้างloading = trueอีกครั้งอยู่ที่ไหน
Bhargav

10
อย่าลืมเพิ่มส่วนloading = trueหลัง //Do paginationมิฉะนั้นรหัสนี้จะทำงานเพียงครั้งเดียวและคุณไม่สามารถโหลดรายการเพิ่มเติมได้
Shaishav Jogani

165

ทำตัวแปรเหล่านี้

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

ตั้งเป็น Scroll เพื่อรับมุมมองรีไซเคิล

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        visibleItemCount = mRecyclerView.getChildCount();
        totalItemCount = mLayoutManager.getItemCount();
        firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount) 
            <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached

            Log.i("Yaeye!", "end called");

            // Do something

            loading = true;
        }
    }
});

หมายเหตุ:ให้แน่ใจว่าคุณกำลังใช้เป็นผู้จัดการการจัดวางสำหรับLinearLayoutManagerRecyclerView

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

สำหรับตัวอย่างโค้ดแบบเต็มโปรดอ้างอิงrepo Githubนี้


14
มีความคิดวิธีการใช้โซลูชันนี้กับ GridLayoutManager หรือไม่? ตั้งแต่findFirstVisibleItemPosition()วิธีการจะใช้ได้เฉพาะกับ LinearLayoutManager ที่ฉันไม่สามารถคิดออกว่าจะทำให้การทำงาน
MathieuMaree

1
ข้อมูลเพิ่มเติม: ในกรณีนี้ใช้พฤติกรรมเหมือนกับการใช้visibleItemCount = mLayoutManager.getChildCount(); visibleItemCount = mRecyclerView.getChildCount();
Jing Li

3
วางรหัสในเมธอด onScrollStateChanged () แทนเมธอด onScrolled ()
Anirudh

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 ตรวจสอบการดึงเพื่อรีเฟรช
Hai Zhang

1
@EpicPandaForce ถ้าคุณกำลังใช้RecyclerViewคุณจะโชคดีอีกครั้งเพราะคุณสามารถทำสำเนาคงที่ของตั้งแต่วิธีการที่เกี่ยวข้องมีการทำเครื่องหมายสาธารณะแทนการคุ้มครองในView.canScrollVertically() RecyclerViewคุณสามารถขยายRecyclerViewเพื่อนำไปใช้ใหม่ได้canScrollVertically()หากต้องการ recyclerView.getLayoutManager().canScrollVertically()วิธีที่สามและที่ง่ายคือการโทร แก้ไขในคำตอบของฉัน
Hai จาง

67

นี่คือวิธีการอื่น มันจะทำงานร่วมกับผู้จัดการรูปแบบใด ๆ

  1. สร้างคลาสอะแด็ปเตอร์เป็นนามธรรม
  2. จากนั้นสร้างเมธอด abstract ในคลาสอะแด็ปเตอร์ (เช่น. load ())
  3. ในonBindViewHolderตรวจสอบตำแหน่งหากล่าสุดและ call load ()
  4. แทนที่ฟังก์ชัน load () ในขณะที่สร้างอแด็ปเตอร์วัตถุในกิจกรรมหรือส่วนของคุณ
  5. ในฟังก์ชั่นการโอเวอร์โหลดใช้การเรียกโหลดมอร์ของคุณ

เพื่อทำความเข้าใจรายละเอียดฉันเขียนโพสต์บล็อกและตัวอย่างโครงการรับได้ที่นี่ 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) วิธีนี้คุณสามารถโหลดได้ทุกตำแหน่ง: ที่ N, N-1, N-2 ..
Samer

1
@mcwise เพิ่มบูลีนเสร็จแล้ว = false; เมื่อคุณมาถึงจุดสิ้นสุดทำให้เสร็จสิ้น = จริง .. เปลี่ยนกระแสหากเงื่อนไขเป็นถ้า ((ตำแหน่ง> = getItemCount () - 1) && (เสร็จ!))
Sabeer Mohammed

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

1
ใน onScrolled ของคุณคุณเลือก LayoutManager จาก RecyclerView เป็น LinearLayoutManager มันไม่ทำงานสำหรับ StaggredGridLayout หรือ anthing อื่น ๆ ไม่มี findFirstVisibleItemPosition () ใน StaggredGridLayout
Orri

@Orri ขอบคุณสำหรับการชี้ให้เห็น! แต่ผมไม่คิดว่ามันเป็นเช่นความคิดที่ไม่ดีเพราะมันทำงานเพื่อฉันและตามไป upvotes กรณีอื่น ๆ :)
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

โปรดให้วิธีแก้ปัญหาสำหรับ recyclerview ด้วยหมายเลขหน้าโหลดมอร์ดผ่าน android
Harsha

getWebServiceData (); mStoresAdapterData.setOnLoadMoreListener (ใหม่ StoresAdapterOnLoadMoreListener () {@Override โมฆะสาธารณะ onLoadMore () {// เพิ่มโมฆะดังนั้นอะแดปเตอร์จะตรวจสอบ view_type และแสดงแถบความคืบหน้าที่ด้านล่าง storesList.add (null); mStoresAdapterData.setOnLoadMoreListener () - 1); ++ pageNumber; getWebServiceData ();}}); ทั้งสองเรียกข้อมูลอีกหน้าในที่สุดก็มองเห็นเพียง 2 ช่วย
Harsha

ฉันลองใช้งาน recyclerView หลายหน้า แต่ไม่มีใครทำงานยกเว้นคุณมี upvote ท่าน
Mohammed Elrashied

9

คำตอบส่วนใหญ่จะถือว่าการRecyclerViewใช้LinearLayoutManagerหรือGridLayoutManagerหรือแม้กระทั่งStaggeredGridLayoutManagerหรือสมมติว่าการเลื่อนเป็นแนวตั้งหรือ horyzontal แต่ไม่มีใครโพสต์คำตอบทั่วไปอย่างสมบูรณ์ไม่มีใครได้โพสต์คำตอบที่สมบูรณ์ทั่วไป

การใช้อViewHolderแด็ปเตอร์นั้นไม่ใช่ทางออกที่ดี อะแดปเตอร์อาจมีมากกว่า 1 RecyclerViewใช้ มัน "ปรับ" เนื้อหาของพวกเขา ควรเป็น Recycler View (ซึ่งเป็นคลาสที่รับผิดชอบสิ่งที่แสดงให้ผู้ใช้ในปัจจุบันไม่ใช่อะแดปเตอร์ที่รับผิดชอบเฉพาะในการให้เนื้อหาแก่RecyclerView) ซึ่งจะต้องแจ้งให้ระบบของคุณทราบว่าจำเป็นต้องมีรายการเพิ่มเติม โหลด)

นี่คือโซลูชันของฉันโดยไม่ใช้สิ่งใดนอกจากคลาส abstracted ของ 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 อื่น - ย้ายไปที่onScrollStateChangedonScrolledแทน และในการonScrollStateChangedตรวจสอบเพียง:if(newState == RecyclerView.SCROLL_STATE_IDLE) { // calculation code }
Trancer

ขอบคุณสำหรับคำแนะนำของคุณ แต่ฉันไม่คิดว่ามันจะเป็นทางออกที่ดีกว่า นักพัฒนาต้องการให้ผู้ฟังเรียกใช้ก่อนที่ recyclerview จะหยุดเพื่อเลื่อน ดังนั้นมันอาจเสร็จสิ้นการโหลดรายการข้อมูลใหม่ (ถ้าข้อมูลถูกเก็บไว้ใน localy) หรือเพิ่มมุมมองแถบความคืบหน้าในตอนท้ายของ recyclerview ดังนั้นเมื่อ recyclerview จะหยุดเพื่อเลื่อนมันสามารถอยู่ในความคืบหน้าเป็นวงกลม -bar เป็นรายการที่มองเห็นได้ล่าสุด
Yairopro

6

แม้ว่าคำตอบที่ได้รับการยอมรับจะทำงานได้อย่างสมบูรณ์แบบโซลูชันด้านล่างใช้ addOnScrollListener เนื่องจาก setOnScrollListener เลิกใช้แล้วและลดจำนวนตัวแปรและหากมีเงื่อนไข

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 () ความคิดใด ๆ ว่าทำไมมันถูกเรียกว่าสามครั้งและวิธีหยุดเรียกมันหลายครั้ง
ved

dy> 0 เป็นเท็จเสมอ
Dushyant Suthar

@ved นั่นเป็นเพราะการเลื่อนมี 3 สถานะ: เลื่อน, เลื่อน, ไม่ได้ใช้งาน คุณเลือกหนึ่งที่เหมาะสม
TheRealChx101

5

แม้ว่าจะมีคำตอบสำหรับคำถามมากมาย แต่ฉันต้องการแบ่งปันประสบการณ์ของเราในการสร้างมุมมองรายการที่ไม่มีที่สิ้นสุด เมื่อเร็ว ๆ นี้เราได้ติดตั้ง Carousel LayoutManager ที่กำหนดเองซึ่งสามารถทำงานในรอบได้โดยการเลื่อนรายการไปเรื่อย ๆ รวมถึงจนถึงจุดหนึ่ง นี่คือคำอธิบายโดยละเอียดเกี่ยวกับ GitHubGitHub

ฉันขอแนะนำให้คุณดูที่บทความนี้พร้อมคำแนะนำสั้น ๆ แต่มีคุณค่าในการสร้าง LayoutManagers ที่กำหนดเอง: http://cases.azoft.com/create-custom-layoutmanager-android/


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 บรรทัด

เมื่อฉันดึงSwipeRefreshLayout's setOnRefreshListener the addOnScrolledToEnd is not working คลาสส่วนตัวภายใน PullToRefresh: SwipeRefreshLayoutOnRefreshListener {override fun onRefresh () {pbViewMore !!. ทัศนวิสัย = View.GONE pageCount = 0 courseList.clear () // เรียก asynctask ที่นี่ srlNoMessageRefreshLayout !! ! .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

ตกลงฉันทำได้โดยใช้onBindViewHolderเมธอดของ RecyclerView.Adapter

อะแดปเตอร์:

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 ทำมุมมองแคช + มีตัวจัดการเลย์เอาต์บางตัวมีความหมายสำหรับการแคชล่วงหน้า ฉันอยากจะแนะนำให้ตั้งค่า listener scroll และเมื่อ listener scroll หยุดรับตำแหน่ง item สุดท้ายที่มองเห็นได้จาก layout manager หากคุณใช้ตัวจัดการเลย์เอาต์เริ่มต้นพวกเขาทั้งหมดมีเมธอด findLastVisible **
yigit

@yigit RecyclerView จะพยายามตรวจสอบสิ่งที่อยู่ไกลออกไปได้อย่างไร หากคุณมี 500 รายการฉันสงสัยจริงๆว่ามันจะเก็บไว้ไกลมาก (ไม่อย่างนั้นมันจะเป็นการเสียประสิทธิภาพอย่างมาก) เมื่อไหร่ที่มันจะเริ่มทำการแคช (แม้ว่าเราจะอยู่ที่องค์ประกอบ 480) นั่นหมายความว่าถึงเวลาแล้วที่จะต้องโหลดชุดอื่นต่อไป
Alex Orlov

1
มันทำสิ่งต่างๆก่อนทำการแคชเว้นแต่ว่ามันจะเลื่อนไปยังตำแหน่งที่ห่างไกลอย่างราบรื่น ในกรณีนั้นรูปแบบ LLM (โดยค่าเริ่มต้น) จะออกสองหน้าแทนที่จะเป็นหนึ่งหน้าเพื่อให้สามารถค้นหามุมมองเป้าหมายและลดความเร็วลง มันจะดูแคชที่เกินขอบเขต โดยค่าเริ่มต้นขนาดแคชนี้คือ 2 และสามารถกำหนดค่าผ่าน setItemCacheSize
yigit

1
@igit เพียงแค่คิดผ่านปัญหาฉันไม่เห็นว่าแคชสามารถนำเสนอปัญหาที่นี่; เราสนใจเฉพาะในครั้งแรกที่รายการ "สุดท้าย" ถูกผูกไว้! ไม่มีการเรียกไปยัง onBindViewHolder เมื่อตัวจัดการโครงร่างใช้มุมมองแคชที่ถูกผูกไว้แล้วนั้นไม่ใช่ปัญหาเมื่อเราสนใจเพจจิ้งในข้อมูลมากขึ้นในตอนท้ายของรายการ แก้ไข?
เกร็กเอนนิส

2
ในไลบรารี FlexibleAdapter ของฉันฉันเลือกที่จะใช้การเลื่อนที่ไม่มีที่สิ้นสุดกับการโทรเข้า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 เป็นรายการอาร์เรย์ของรายการ แต่ฉันจะหาตำแหน่งได้อย่างไร
BooleanCheese

คุณสามารถดูวิธีที่ฉันใช้ RecyclerView.Adapter ของฉันได้ที่นี่: github.com/dominicthomas/FlikrGridRecyclerView/blob/master/app/
......

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

มีไลบรารีที่ใช้งานง่ายสำหรับเพจที่มีชื่อนี้ รองรับทั้ง ListView และ RecyclerView (LinearLayout, GridLayout และ StaggeredGridLayout)

นี่คือลิงค์ไปยังโครงการบน Github


2
  1. สร้างคลาสนามธรรมและขยาย RecyclerViewOnScrollListener

    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

นี่เป็นวิธีแก้ปัญหาของฉันโดยใช้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เป็นเท็จอีกครั้ง
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

ฉันได้สร้าง LoadMoreRecyclerView โดยใช้Abdulaziz Noor Answer

LoadMoreRecyclerView

public class LoadMoreRecyclerView extends RecyclerView  {

    private boolean loading = true;
    int pastVisiblesItems, visibleItemCount, totalItemCount;
    //WrapperLinearLayout is for handling crash in RecyclerView
    private WrapperLinearLayout mLayoutManager;
    private Context mContext;
    private OnLoadMoreListener onLoadMoreListener;

    public LoadMoreRecyclerView(Context context) {
        super(context);
        mContext = context;
        init();
    }

    public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
    }

    public LoadMoreRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        init();
    }

    private void init(){
        mLayoutManager = new WrapperLinearLayout(mContext,LinearLayoutManager.VERTICAL,false);
        this.setLayoutManager(mLayoutManager);
        this.setItemAnimator(new DefaultItemAnimator());
        this.setHasFixedSize(true);
    }

    @Override
    public void onScrolled(int dx, int dy) {
        super.onScrolled(dx, dy);

        if(dy > 0) //check for scroll down
        {
            visibleItemCount = mLayoutManager.getChildCount();
            totalItemCount = mLayoutManager.getItemCount();
            pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();

            if (loading)
            {
                if ( (visibleItemCount + pastVisiblesItems) >= totalItemCount)
                {
                    loading = false;
                    Log.v("...", "Call Load More !");
                    if(onLoadMoreListener != null){
                        onLoadMoreListener.onLoadMore();
                    }
                    //Do pagination.. 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.RecyclerViewOnScrollListener 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
โปรดเจาะจงมากขึ้น!ฉันจะใช้RecyclerViewOnScrollListenerเพื่อตรวจสอบว่าผู้ใช้เลื่อนไปที่ส่วนท้ายของรายการได้อย่างไร
erdna

ไม่มี onScroll ที่RecyclerViewOnScrollListener ! คุณผสม AbsListViewOnScrollListener กับ RecyclerViewOnScrollListener
erdna

ใช่มันควรจะเป็นonScrollStateChanged
Panther

onScrollStateChangedจะไม่ทำงานเนื่องจากจะถูกเรียกเมื่อคุณเปลี่ยนสถานะของการเลื่อนเท่านั้นและจะไม่ทำงานในขณะที่คุณเลื่อน
Pedro Oliveira

@PedroOliveira เป็นไปได้หรือไม่ที่onScrollStateChangedจะพิจารณาว่าผู้ใช้เลื่อนไปที่รายการสุดท้ายหรือไม่?
erdna

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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.