รู้สึกว่า RecyclerView ในเวอร์ชันหลังๆจะมีของเล่นใหม่ๆให้ใช้งานเยอะเหลือเกินนะ แต่ก็ถือว่าเป็นเรื่องดีแหละที่นักพัฒนาจะได้สะดวกสบายมากขึ้นจากการใช้งาน RecyclerView

ซึ่งในคราวนี้ก็จะมาพูดถึงคลาสที่มีชื่อว่า SnapHelper กัน

เพราะ RecyclerView แบบปกติไม่รองรับการ Snapping

เวลาสร้าง RecyclerView ขึ้นมาซักตัวหนึ่งจะพบว่าต่อให้เลื่อนให้อยู่ตำแหน่งไหนก็ตาม เมื่อยกนิ้วออก มันก็จะหยุดอยู่ที่ตำแหน่งนั้นทันที ต่อให้เลื่อนค้างไว้แบบครึ่งๆกลางๆ มันก็จะหยุดอยู่แบบนั้นแหละ

แต่บ่อยครั้งนักพัฒนาก็อยากจะให้ผู้ใช้ลากค้างไว้ที่ตำแหน่งก็ได้ แล้วให้ RecycleView เลื่อนให้ข้อมูลชิดด้านใดด้านนึงของจอโดยอัตโนมัติ หรือที่เรียกกันว่า Snapping นั่นเอง

จึงทำให้นักพัฒนาบางคนเปลี่ยนไปใช้ ViewPager แทน เพราะว่ารองรับการ Snapping ในตัวอยู่แล้วนั่นเอง แต่ทว่า ViewPager ก็ไม่เหมาะกับข้อมูลที่แสดงไม่เต็มพื้นที่หน้าจอซักเท่าไร

ดังนั้นทีมพัฒนาจึงได้เพิ่มสิ่งที่เรียกว่า SnapHelper ให้กับ RecyclerView เพื่อให้นักพัฒนากำหนดการ Snapping ของข้อมูลใน RecyclerView ได้ง่ายๆ

รู้จักกับคลาส SnapHelper

SnapHelper จะควบคุมการ Fling  (การปัดนิ้วเพื่อเลื่อนข้อมูล) หลังจากที่ผู้ใช้ยกนิ้วออกจากหน้าจอแล้ว เพื่อคำนวณว่าจะให้ข้อมูลตัวไหนเลื่อนไปอยู่ในตำแหน่งที่ต้องการให้ Snapping ไว้

โดย SnapHelper ที่มาพร้อมกับ RecyclerView จะมีให้เลือกใช้งานทั้งหมด 2 แบบด้วยกัน คือ

  • LinearSnapHelper : ใช้ทำ Snapping แบบทั่วไป รองรับทั้งแนวตั้งและแนวนอน
  • PagerSnapHelper : ใช้ทำ Snapping แบบ ViewPager ใช้กับ Item View ที่กำหนดขนาดเป็น Match Parent เท่านั้น

การใช้งาน LinearSnapHelper

ในการเรียกใช้งาน LinearSnapHelper ก็เพียงแค่สร้างขึ้นมาแล้วกำหนด RecyclerView ที่ต้องการด้วยคำสั่ง attachToRecyclerView(...) ได้เลย

val recyclerView: RecyclerView = /* ... */
val snapHelper = LinearSnapHelper()
snalHelper.attachToRecyclerView(recyclerView)

เพียงเท่านี้ RecyclerView ตัวนี้ก็จะรองรับ Snapping เรียบร้อยแล้ว

ถ้าใช้กับ RecyclerView แนวนอนล่ะ?

ทีนี้ลองใช้กับ RecyclerView แนวนอนดูบ้างว่า LinearSnapHelper ทำงานได้ตามที่ต้องการหรือป่าว

ผลลัพธ์ที่ได้ก็จะเป็นแบบนี้แทน

จะเห็นว่าข้อมูลจะ Snapping อยู่ที่ตรงกลางของหน้าจอแทน ไม่ได้ชิดด้านซ้ายหรือด้านขวาเลย และเมื่อไม่สามารถชิดด้านใดด้านหนึ่งได้ ก็จะเกิดปัญหาว่าผู้ใช้สามารถเลื่อนข้อมูลตัวแรกสุดและตัวสุดท้ายให้ชิดขอบได้เลย (เพราะตัวมันเองอยู่สุดขอบแล้ว เลื่อนต่อไม่ได้แล้ว)

แล้วแบบนี้จะทำยังไงดีล่ะ?

เปลี่ยนมาใช้ GravitySnapHelper แทน

จะไปสร้าง SnapHelper ขึ้นมาเองก็คงเสียเวลาโดยใช่เหตุ เพราะจริงๆแล้วมีนักพัฒนาคนอื่นได้ทำ SnapHelper ที่ชื่อว่า GravitySnapHelper ขึ้นมาให้ใช้งานแล้ว เพื่อให้นักพัฒนาทำ Snapping ในแนวนอนได้

rubensousa/GravitySnapHelper
A SnapHelper that snaps a RecyclerView to an edge. - rubensousa/GravitySnapHelper

เริ่มจากเพิ่ม Dependency ของ Library ตัวนี้ไว้ใน build.gradle ก่อน

implementation 'com.github.rubensousa:gravitysnaphelper:2.2.1'

โดย GravitySnapHelper จะกำหนดทิศทางในการ Snapping ได้ทั้งหมด 4 ด้านด้วยกัน (สะดวกสุดๆ)

  • Gravity.TOP กับ Gravity.BOTTOM สำหรับแนวตั้ง
  • Gravity.START กับ Gravity.END สำหรับแนวนอน

และเวลาเรียกใช้งานก็จะเป็นแบบนี้

val recyclerView: RecyclerView = /* ... */
val snapHelper = GravitySnapHelper(Gravity.START)
snapHelper.attachToRecyclerView(recyclerView)

เพียงเท่านี้ก็จะได้ Snapping ชิดฝั่งซ้ายแบบที่เจ้าของบล็อกอยากจะได้แล้ว เย้!

การใช้งาน PagerSnapHelper

สำหรับ PagerSnapHelper จะเป็น SnapHelper ที่ใช้กับ RecyclerView ที่แสดงข้อมูลในรูปแบบของ Pager ที่มีการเรียกใช้งานแบบนี้

val recyclerView: RecyclerView = /* ... */
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)

โดยมีเงื่อนไขว่า ItemView ใน RecyclerView จะต้องมีขนาดเต็มพื้นที่ของด้านใดด้านนึงโดยอ้างอิงจากทิศทางที่กำหนดไว้ใน LayoutManager

  • ถ้ากำหนดทิศทางเป็นแนวนอน จะต้องกำหนดความกว้างให้เต็มพื้นที่
  • ถ้ากำหนดทิศทางเป็นแนวตั้ง จะต้องกำหนดความสูงให้เต็มพื้นที่

ถึงแม้ว่า PagerSnapHelper จะเป็นอีกตัวหนึ่งที่นักพัฒนาสามารถใช้งานได้ก็จริง แต่เจ้าของบล็อกแนะนำให้ใช้ ViewPager2 แทนดีกว่า เพราะเบื้องหลังคือ RecyclerView + PagerSnapHelper นั่นเอง

สรุป

SnapHelper เป็นหนึ่งในความสามารถของ RecyclerView ที่จะเข้ามาช่วยให้การทำ Snapping ตำแหน่งข้อมูลใน RecyclerView ได้ง่ายๆ จากเดิมที่นักพัฒนาจะต้องเขียนโค้ดต่างๆมากมาย ก็จะเหลือโค้ดแค่ไม่กี่บรรทัด

ในกรณีที่ใช้งานกับ RecyclerView แนวตั้งก็สามารถใช้ LinearSnapHelper ได้เลย แต่ถ้าเป็นแนวนอนก็อาจจะต้องเปลี่ยนไปใช้ GravitySnapHelper แทน

และถ้าจะใช้ PagerSnapHelper ก็แนะนำให้เปลี่ยนจาก RecyclerView ไปใช้ ViewPager2 แทนเลยดีกว่านะ

แหล่งข้อมูลอ้างอิง