RecyclerView.ViewHolder - getLayoutPosition เทียบกับ getAdapterPosition


90

ตั้งแต่ไลบรารีการสนับสนุนเวอร์ชันใหม่ (22.x) getPosition()เมธอดของRecyclerView.ViewHolderคลาสจึงถูกเลิกใช้แทนวิธีการที่กล่าวถึงในหัวข้อ ฉันไม่ค่อยได้รับความแตกต่างจากการอ่านเอกสาร ใครช่วยอธิบายความแตกต่างในแง่ของคนธรรมดาได้ไหม?

ฉันมีกรณีการใช้งานต่อไปนี้ - ฉันให้อะแดปเตอร์ของฉันListและยังต้องการเชื่อมโยงข้อมูลเพิ่มเติมสำหรับแต่ละรายการ ฉันมีการทำแผนที่แบบกำหนดตำแหน่งเพิ่มเติมและมีการทำแผนที่สำหรับผู้ถือครองเพื่อให้พวกเขาสามารถดึงข้อมูลเพิ่มเติมสำหรับตำแหน่งของพวกเขาและทำสิ่งต่างๆได้ ในที่ยึดควรใช้วิธีใด

จะเกิดอะไรขึ้นกับตำแหน่งผู้ถือเมื่อรายการที่ดัชนี 0 และ 1 เปลี่ยนตำแหน่ง วิธีการคืนค่า?

คำตอบ:


116

นี่เป็นสถานการณ์ที่ยุ่งยากขออภัยที่เอกสารไม่เพียงพอ

เมื่อเนื้อหาของอะแด็ปเตอร์เปลี่ยน (และคุณเรียกใช้notify***()) RecyclerView ร้องขอเค้าโครงใหม่ ตั้งแต่ช่วงเวลานั้นจนกระทั่งระบบโครงร่างตัดสินใจคำนวณโครงร่างใหม่ (<16 ms) ตำแหน่งโครงร่างและตำแหน่งอะแดปเตอร์อาจไม่ตรงกันเนื่องจากเค้าโครงยังไม่สะท้อนการเปลี่ยนแปลงอะแด็ปเตอร์

ในกรณีที่การใช้งานของคุณเนื่องจากข้อมูลของคุณที่เกี่ยวข้องกับเนื้อหาของอะแดปเตอร์ของคุณ (และผมถือว่าข้อมูลที่มีการเปลี่ยนแปลงในเวลาเดียวกันกับการเปลี่ยนแปลงอะแดปเตอร์), adapterPositionคุณควรจะใช้

อย่างไรก็ตามโปรดระวังหากคุณกำลังโทรnotifyDataSetChanged()เพราะมันทำให้ทุกอย่างไม่ถูกต้อง RecyclerView ไม่ทราบว่าตำแหน่งอะแดปเตอร์ของ ViewHolder จนกว่าจะมีการคำนวณเค้าโครงถัดไป ในกรณีนั้นgetAdapterPosition()จะส่งคืนRecyclerView#NO_POSITION( -1)

แต่สมมติว่าคุณโทรnotifyItemInserted(0)หาgetAdapterPosition()ViewHolder ซึ่งก่อนหน้านี้อยู่ในตำแหน่ง0จะเริ่มกลับมา1ทันที ตราบเท่าที่คุณจัดส่งเหตุการณ์การแจ้งเตือนแบบละเอียดคุณจะอยู่ในสถานะที่ดีเสมอ (เราทราบตำแหน่งอะแดปเตอร์แม้ว่าจะยังไม่ได้คำนวณรูปแบบใหม่ก็ตาม)

อีกตัวอย่างหนึ่งหากคุณกำลังทำอะไรบางอย่างกับการคลิกของผู้ใช้หากgetAdapterPosition()ส่งคืนNO_POSITIONคุณควรเพิกเฉยต่อการคลิกนั้นเนื่องจากคุณไม่ทราบว่าผู้ใช้คลิกอะไร (เว้นแต่คุณจะมีกลไกอื่น ๆ เช่นรหัสที่เสถียรเพื่อค้นหารายการ)

แก้ไขเมื่อตำแหน่งเลย์เอาต์ดี

สมมติว่าคุณกำลังใช้งานLinearLayoutManagerและต้องการเข้าถึง ViewHolder เหนือรายการที่คลิกในปัจจุบัน ในกรณีนั้นคุณควรใช้ตำแหน่งเค้าโครงเพื่อรับรายการด้านบน

mRecyclerView.findViewHolderForLayoutPosition(myViewHolder.getLayoutPosition() - 1)

คุณต้องใช้ตำแหน่งเค้าโครงเพราะตรงกับสิ่งที่ผู้ใช้กำลังเห็นบนหน้าจอ


1
ฉันเล่นไปสักพักและปรากฎว่าเมธอด getAdapterPosition () จะคืนค่า -1 ให้ฉันเสมอ ฉันดีบั๊กและเหตุผลก็คือโค้ดในเมธอด (Final ViewParent parent = itemView.getParent (); if (! (parent instanceof RecyclerView)) {return -1;} จะเข้าสู่ if block เสมอนั่นคือมุมมองผู้รีไซเคิล ไม่ใช่พาเรนต์ของมุมมองเซลล์ของฉันเป็นไปได้อย่างไรโค้ดของฉันในการสร้างโฮลเดอร์คือ: return MyViewHolder (LayoutInflater.from (viewGroup.getContext ()) พอง (R.layout.test_list_item, viewGroup, false)); (มีต่อในความคิดเห็นอื่น)
wujek

เมื่อฉันเปลี่ยนรหัสเพื่อเรียกขยาย (R.layout.test_list_item, viewGroup, true); (สังเกตความจริงสำหรับ 'แนบกับรูท), Android พ่น: java.lang.IllegalStateException: เด็กที่ระบุมีผู้ปกครองอยู่แล้ว คุณต้องเรียก removeView () บนผู้ปกครองของเด็กก่อน ดังนั้นวิธีที่ถูกต้องในการสร้างตัวยึดมุมมองที่มีมุมมองที่แนบมากับมุมมองรีไซเคิลอย่างถูกต้องคืออะไร? แปลกพอสมควรแม้ว่า getAdapterPosition () จะคืนค่า -1 เนื่องจากพาเรนต์เป็นโมฆะ แต่อย่างอื่นก็ใช้ได้ดี
wujek

1
พารามิเตอร์บูลีนในตัวขยายโครงร่างคือ "addToParent" ต้องเป็นเท็จเนื่องจากเป็นความรับผิดชอบของ LayoutManager ในการเพิ่ม ฉันคิดว่าคุณกำลังเรียก getAdapterPosition ใน onBind ซึ่งคุณได้ผ่านตำแหน่งไปแล้ว ในทางเทคนิคผู้ถือมุมมองแสดงถึงตำแหน่งนั้นหลังจากส่งคืน onBind Btw เราได้อัปเดตตำแหน่ง getAdapter เพื่อส่งคืนตำแหน่งที่ถูกต้อง (ถ้าเป็นไปได้) แม้ว่าจะถูกปลดออก แต่ก็จะออกในเร็ว ๆ นี้
yigit

คุณพูดถูกฉันเรียก getAdapterPosition ใน onBind ฉันควรทำอย่างไรในกรณีนี้แทนฉันต้องการตำแหน่งที่จะได้รับข้อมูลบางอย่างที่มีผลต่อสถานะของมุมมองบางอย่าง การเรียก getLayoutPosition ในกรณีนี้ใช้ได้หรือไม่
wujek

5
คุณควรใช้พารามิเตอร์ตำแหน่งที่ส่งผ่านไปยังเมธอด onBind
yigit

2


เพื่อยืนยันความแตกต่าง (s) ของgetAdapterPosition(), getLayoutPosition()และยังposition; เราจะสังเกตเห็นกรณีด้านล่าง:

1. positionอาร์กิวเมนต์ในonBindViewHolder()วิธีการ:

เราสามารถใช้การpositionผูกข้อมูลเข้ากับข้อมูลพร็อพเพอร์ตี้และสามารถใช้positionอาร์กิวเมนต์เพื่อทำสิ่งนี้ได้ แต่ไม่เป็นไรที่จะใช้positionอาร์กิวเมนต์เพื่อจัดการกับการคลิกของผู้ใช้และหากคุณใช้คุณจะเห็นคำเตือนบอกคุณว่า "ไม่ถือว่าpositionเป็น คงที่และใช้holder.getAdapterPosition()แทน ".

2. getAdapterPosition():

วิธีนี้ประกอบด้วยตำแหน่งของอะแดปเตอร์ที่อัปเดตของไฟล์holder. positionมันหมายความว่าเมื่อใดก็ตามที่คุณคลิกในรายการคุณถามอะแดปเตอร์เกี่ยวกับมัน ดังนั้นคุณจะได้รับตำแหน่งล่าสุดของรายการนี้ในแง่ของตรรกะของอะแดปเตอร์

3. getLayoutPosition():

บางครั้งจำเป็นต้องค้นหาpositionในแง่ของเลย์เอาต์ที่อัปเดตแล้ว (เลย์เอาต์ที่ส่งผ่านล่าสุดที่ผู้ใช้เห็นในขณะนี้) ตัวอย่างเช่นหากผู้ใช้ขอสิ่งที่สามpositionเขาสามารถมองเห็นและคุณใช้swipe/ dismissสำหรับรายการหรือใช้ภาพเคลื่อนไหวใด ๆ หรือของประดับตกแต่งสำหรับสิ่งของต่างๆมันจะดีกว่าถ้าใช้getLayoutPosition()แทนgetAdapterPosition()เพราะคุณจะต้องแน่ใจเสมอว่าคุณกำลังจัดการกับตำแหน่งของไอเทมในแง่ของเลย์เอาต์ที่ส่งผ่านล่าสุด


สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ ดูที่นี่ . . .

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