รับรายการที่มองเห็นได้ใน RecyclerView


268

ฉันต้องรู้ว่าองค์ประกอบใดบ้างที่จะแสดงใน RecyclerView ของฉัน ไม่มีOnScrollListener.onScroll(...)วิธีเทียบเท่ากับ ListViews ฉันพยายามทำงานด้วยView.getGlobalVisibleRect(...)แต่แฮ็คนั้นน่าเกลียดเกินไปและก็ไม่ได้ผลเสมอไป

มีใครคิดบ้างไหม?

คำตอบ:


579

ชื่อ / LayoutManagerนามสกุลเด็กสามารถมองเห็นได้ขึ้นอยู่กับ หากคุณกำลังใช้LinearLayoutManagerหรือGridLayoutManagerคุณสามารถใช้

int findFirstVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();

ตัวอย่างเช่น:

GridLayoutManager layoutManager = ((GridLayoutManager)mRecyclerView.getLayoutManager());
int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();

สำหรับLinearLayoutManager, ครั้งแรก / ครั้งสุดท้ายขึ้นอยู่กับการสั่งซื้ออะแดปเตอร์ อย่าสอบถามเด็ก ๆ จากRecyclerView; LayoutManagerอาจต้องการจัดวางรายการมากกว่าที่มองเห็นสำหรับการแคช


10
มีความเป็นไปได้ที่จะเข้าถึงวิธีนี้จาก RecyclerView.Adapter โดยไม่ผ่านการอ้างอิงไปยัง LayoutManager หรือไม่?
Yorgi

3
ชอบ @Yorgi - ภายในอะแดปเตอร์ดูเหมือนว่ามีประโยชน์ ฉันสงสัยว่ามีรูปแบบบางอย่างที่คุณสามารถพัฒนาโดยใช้ onBindViewHolder เพื่อติดตามว่าอันไหนบนหน้าจอจริงในขณะที่ผู้ใช้เลื่อน
RoundSparrow hilltx

7
getItemCountสามารถส่งคืน 1 และfindFirstVisibleItemPosition-1 ในการโทรเดียวกัน
mbmc

4
คุณสามารถแนะนำอะไรเกี่ยวกับ StaggeredLayoutManager ได้บ้าง?
hornet2319

37
วิธีการเหล่านี้ไม่น่าเชื่อถือดังนั้นจึงไร้ประโยชน์อย่างสมบูรณ์ โดยเฉพาะอย่างยิ่งหลังจากแจ้ง DataSetChanged / itemRemoved / RangeChanged
JK

12

ในที่สุดฉันก็พบวิธีแก้ปัญหาที่จะรู้ว่ารายการปัจจุบันสามารถมองเห็นได้จากเหตุการณ์ onBindViewHolder ในอะแดปเตอร์

ที่สำคัญคือวิธีการคือViewPartiallyVisibleจาก LayoutManager

ในอะแดปเตอร์ของคุณคุณสามารถรับ LayoutManager จาก RecyclerView ซึ่งคุณได้รับเป็นพารามิเตอร์จากเหตุการณ์onAttachedToRecyclerView


แต่คุณจะเรียกวิธีนี้ในที่ซึ่งตัวจัดการโครงร่างไม่ได้ว่างเปล่าที่ไหน?
6

คุณต้องรอให้เหตุการณ์ onAttachedToRecyclerView ของอะแดปเตอร์ ในกรณีนั้นคุณจะได้รับ RecyclerView เป็นพารามิเตอร์ จากนั้นคุณสามารถรับ LayoutManager จาก RecyclerVie และบันทึกไว้ใน var ส่วนกลาง หาก LayoutManager นั้นเป็นโมฆะอาจเป็นเพราะมันไม่ได้ถูกกำหนดให้กับ RecyclerView ใน Activity / Fragment / Layout และพยายามสร้างอะแดปเตอร์หลังจากที่ได้รับมอบหมาย
Ernesto Vega

ตามซอร์สโค้ด isViewPartiallyVisible เสียอย่างรุนแรง ถ้า completeVisible นั้นเป็นจริงมันจะกลับเป็นจริงหากมุมมองนั้นมองเห็นได้อย่างสมบูรณ์ อย่างไรก็ตามหากเป็นเท็จมันจะลบล้างผลลัพธ์ที่ส่งกลับค่าจริงหากมุมมองสามารถมองเห็นได้บางส่วนหรือมองไม่เห็น แม้แต่เอกสารก็ไม่สมเหตุสมผล
Molanda


9

สำหรับผู้ที่มีตรรกะที่จะใช้งานในอะแดปเตอร์ RecyclerView คุณยังสามารถใช้ @ernesto วิธีการรวมกับ on scrollListener เพื่อรับwhat you wantเป็น RecyclerView ปรึกษา ภายในอะแดปเตอร์คุณจะมีสิ่งนี้:

@Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if(manager instanceof LinearLayoutManager && getItemCount() > 0) {
            LinearLayoutManager llm = (LinearLayoutManager) manager;
            recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                }

                @Override
                public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                        int visiblePosition = llm.findFirstCompletelyVisibleItemPosition();
                        if(visiblePosition > -1) {
                            View v = llm.findViewByPosition(visiblePosition);
                            //do something
                            v.setBackgroundColor(Color.parseColor("#777777"));
                        }
                }
            });
        }
    }

7

คุณสามารถใช้recyclerView.getChildAt()เพื่อให้เด็ก ๆ มองเห็นได้และการตั้งค่าแท็กบางอย่างconvertview.setTag(index)ในมุมมองเหล่านี้ในรหัสอะแดปเตอร์จะช่วยให้คุณเชื่อมโยงกับข้อมูลอะแดปเตอร์


ปัญหาเกี่ยวกับ getChildAt () คือลำดับขององค์ประกอบสามารถออกได้จาก 5 องค์ประกอบ: 0, 5, 4, 3, 2, 1 (จำนวนเด็กเป็นส่วนใหญ่> = จำนวนรายการที่มองเห็นได้) ดังนั้นฉันจึงพยายามรับลำดับขององค์ประกอบผ่าน getGlobalVisibleRect
rekire

ความต้องการที่แท้จริงของคุณคืออะไรรับเด็กที่มองเห็น (เด็กอยู่ภายในหน้าจอที่ถูกผูกไว้) ตามลำดับที่เกิดขึ้นจริง?
Sreejith B Naick

ตอนนี้ฉันอยากรู้ว่าองค์ประกอบใดที่อยู่ในศูนย์กลางหลังจากนั้นฉันก็อาจจะสนใจขอบเขต
rekire

1
คุณจะได้รับไอเท็มที่มองเห็นได้จากrecyclerView.getChildAt()การRecyclerViewทำงานปกติเท่านั้น RecyclerViewจะพยายามเก็บมุมมองชายด์เพียงไม่กี่รายการที่สามารถมองเห็นได้ในปัจจุบัน (เช่นภายในขอบเขตหน้าจอไม่ใช่การมองเห็นเป็น GONE, INVISIBLE) และพยายามรีไซเคิลมุมมองเหล่านี้เมื่อผู้ใช้เลื่อน
Sreejith B Naick

6
ดู visibleChild = recyclerView.getChildAt (0); int positionOfChild = recyclerView.getChildAdapterPosition (visibleChild);
Kevin Lee

4

Linear / Grid LayoutManagerวิธีการดังต่อไปนี้สามารถใช้ในการตรวจสอบรายการที่visible

int findFirstVisibleItemPosition();
int findLastVisibleItemPosition();
int findFirstCompletelyVisibleItemPosition();
int findLastCompletelyVisibleItemPosition();

และถ้าคุณต้องการติดตามis item visible on screenเกณฑ์บางอย่างคุณสามารถอ้างถึงบล็อกต่อไปนี้

https://proandroiddev.com/detecting-list-items-perceived-by-user-8f164dfb1d05


4

ภาคผนวก :

ที่นำเสนอฟังก์ชั่นFindLast ... ตำแหน่ง ()ไม่ได้ทำงานอย่างถูกต้องในสถานการณ์ที่มีแถบเครื่องมือยุบในขณะที่แถบเครื่องมือที่มีการขยายตัว

ดูเหมือนว่ามุมมองรีไซเลอร์มีความสูงคงที่และในขณะที่แถบเครื่องมือถูกขยายรีไซเลอร์จะถูกย้ายลงมาจากหน้าจอบางส่วน ดังนั้นผลลัพธ์ของฟังก์ชั่นที่เสนอจึงสูงเกินไป ตัวอย่าง: ไอเท็มที่มองเห็นได้ครั้งสุดท้ายถูกบอกว่าเป็น # 9 แต่อันที่จริงแล้วไอเท็ม # 7 เป็นรายการสุดท้ายที่อยู่บนหน้าจอ

พฤติกรรมนี้เป็นสาเหตุที่ทำให้มุมมองของฉันไม่สามารถเลื่อนไปยังตำแหน่งที่ถูกต้องได้เช่น scrollToPosition () ทำงานไม่ถูกต้อง (ในที่สุดฉันก็ยุบแถบเครื่องมือโดยทางโปรแกรม)


นั่นเป็นความจริง แต่นี่เป็นวิธีการทำงานของแถบเครื่องมือการยุบ ฉันหยุดพัฒนาแอพ Android เมื่อปีที่แล้ว แต่ฉันจำได้ว่า
rekire

3

สำหรับการStaggeredGridLayoutManagerทำสิ่งนี้:

RecyclerView rv = findViewById(...);
StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(...);
rv.setLayoutManager(lm);

และเพื่อรับมุมมองรายการที่มองเห็นได้:

int[] viewsIds = lm.findFirstCompletelyVisibleItemPositions(null);
ViewHolder firstViewHolder = rvPlantios.findViewHolderForLayoutPosition(viewsIds[0]);
View itemView = viewHolder.itemView;

อย่าลืมตรวจสอบว่ามันว่างเปล่า


คุณหมายถึงอะไร "เช็คว่าว่างหรือไม่" ตรวจสอบได้อย่างไร?
tmm1

viewsIdsเป็นอาร์เรย์และอาจว่างเปล่า
Douglas Nassif Roma Junior
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.