รู้สึกว่า 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 ในแนวนอนได้
เริ่มจากเพิ่ม 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 แทนเลยดีกว่านะ