08 November 2016

รู้จักกับ SnapHelper ของเล่นใหม่ที่เพิ่มเข้ามาใน Recycler View

Updated on


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

ปัญหา Recycler View กับการ Snap ของ View

        ให้ลองนึกว่าผู้ที่หลงเข้ามาอ่านมี Recycler View อยู่ตัวหนึ่ง และต้องการให้มันล็อคตำแหน่ง (Snap) เวลาเลื่อนทุกครั้งแบบนี้


        จะเห็นว่าเวลาเจ้าของบล็อกเลื่อนข้อมูลใน Recycler View ที่ขอบบนของ Recycler View จะ Snap ตำแหน่งของ View ที่อยู่บนสุดให้ชิดกับขอบพอดีทุกครั้ง

        แต่ความเป็นจริงนั้นโหดร้าย เพราะว่า Recycler View ปกติแล้วจะไม่ได้ Snap ตำแหน่งแบบนั้น ไม่ว่าจะเลื่อนยังไงก็ตามตำแหน่งของ View ก็จะหยุดครึ่งๆกลางๆทันที


        การ Snap ตำแหน่งของ View นั้นค่อนข้างเจอได้บ่อยมาก อย่างน้อยก็หน้าดาวน์โหลดแอปฯใน Google Play Store นี่แหละ


        ซึ่งนักพัฒนาส่วนใหญ่จะนึกถึงการใช้ View Pager แทน เพราะว่า View Pager นั้น Snap ตำแหน่งให้ แต่ทว่า View Pager ก็ไม่ได้ตอบโจทย์ซักเท่าไรนัก เนื่องจากตัวมันทำมาเพื่อใช้กับ Fragment ถึงแม้จะสามารถใช้กับ View Group ก็ตาม แต่มันก็ไม่ค่อยเหมาะสมซักเท่าไรที่จะเอามาใช้งานแบบนี้

        ดังนั้นทีมพัฒนาจึงได้สร้างคลาสที่ชื่อว่า SnapHelper ขึ้นมาเพื่อที่จะช่วยจัดการเรื่องนี้ให้ง่ายขึ้นนั่นเอง ซึ่งคลาสดังกล่าวนี้จะมีอยู่ใน Recycler View เวอร์ชัน 24.2.0 ขึ้นไป

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

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

        โดยจะทำงานได้ก็ต่อเมื่อ Layout Manager ที่กำหนดให้ Recycler View มีการ Implement Interface ที่ชื่อว่า RecyclerView.SmoothScroller.ScrollVectorProvider ไว้ ซึ่ง Layout Manager พื้นฐานที่มีให้ไม่ว่าจะเป็น LinearLayoutManager, GridLayoutManager หรือ StaggeredGridLayoutManager ประกาศไว้อยู่แล้ว

        แต่สมมติว่าผู้ที่หลงเข้ามาอ่านสร้าง Layout Manager ของตัวเองขึ้นมาแล้วไม่ได้ประกาศ RecyclerView.SmoothScroller.ScrollVectorProvider ไว้ คลาส LayoutManager ตัวนั้นจะต้อง Override คำสั่ง onFling แล้วเขียนคำสั่งจัดการเอง

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

การใช้งานคลาส SnapHelper

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

        คำสั่งนั้นแสนง่ายมาก เพียงแค่สร้าง Instance ขึ้นมาแล้วผูกเข้ากับ Recycler View ที่ต้องการด้วยคำสั่ง attachToRecyclerView แบบนี้

LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

        เพียงเท่านี้ Recycler View ของผู้ที่หลงเข้ามาอ่านก็จะ Snap ตำแหน่งแล้ว

        แต่ก่อนอื่น เจ้าของบล็อกขอเปลี่ยนรูปแบบการแสดงของ Recycler View ใหม่ก่อน โดยเปลี่ยนให้มาแสดงในแนวนอนแบบนี้แทน



        ทีนี้มาลองดูผลลัพธ์กันดีกว่า


        เอ... รู้สึกเหมือนเจ้าของบล็อกหรือป่าว ว่ามัน Snap ตำแหน่งไม่ตรงอย่างที่คิดไว้....

        เพราะการ Snap ของ LinearSnapHelper นั้นไม่ได้ Snap ตำแหน่งในแบบที่เจ้าของบล็อกต้องการครับ เพราะมันจะ Snap ตำแหน่งของ View ให้อยู่ตรงกลางนั่นเอง


        แล้วเจ้าของบล็อกจะทำยังไงดี เพราะอยากให้ Snap ตำแหน่งของ View ที่ซ้ายมือของหน้าจอ

        เขียน Snap Helper เองเลย!?

        ไม่จำเป็นต้องเขียนเองครับ เพราะมีนักพัฒนาเขียนไว้ให้แล้ว โดยมีชื่อว่า GravitySnapHelper สามารถตามไปดูกันได้ที่ RecyclerViewSnap [GitHub]

compile 'com.github.rubensousa:gravitysnaphelper:1.3'

        สำหรับการใช้งาน GravitySnapHelper นั้นจะต้องมีการกำหนด Gravity ใน Contructor ด้วย เพื่อกำหนดว่าจะให้ Snap ตำแหน่งของ View ที่ฝั่งไหน โดยจะกำหนดได้ 4 ฝั่งด้วยกัน

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

        คำสั่งจะเป็นแบบนี้

GravitySnapHelper snapHelper = new GravitySnapHelper(Gravity.START);
snapHelper.attachToRecyclerView(recyclerView);

        จากคำสั่งข้างบนนี้ก็จะทำให้ View ที่อยู่ใน Recycler View นั้น Snap ที่ฝั่งซ้ายของหน้าจอแล้วจ้า


PagerSnapHelper ทำ Recycler View ให้เหมือน View Pager 

        ในกรณีที่มีเหตุจำเป็นต้องใช้ Recycler View แต่แสดงผลแบบ View Pager ก็จะมีคลาสที่ชื่อว่า PagerSnapHelper ให้ใช้งาน โดยมีเงื่อนไขว่าจะต้องกำหนดขนาดของ Recycler View และ View Holder ให้เป็น Match Parent ทั้งคู่


        ส่วนวิธีเรียกใช้งานก็จะเหมือนกับ SnapHelper ปกติเลย

PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

สรุป

        ถือว่าเป็นเรื่องดีงามที่ Snap Helper จะมาช่วยให้การ Snap ตำแหน่งของ View ใน Recycler View เป็นเรื่องง่ายขึ้น ลดความสิ้นเปลืองจากการใช้ View Pager ได้เป็นอย่างดี อีกทั้งยังสามารถ Custom ได้ตามใจชอบด้วย (แต่เท่าที่มีให้ก็ครอบคลุมแล้วล่ะ)

        สำหรับการใช้งาน ถ้าอยากจะให้ Snap ตำแหน่ง View ที่กลางจอก็ใช้ LinearSnapHelper และถ้าอยากให้ Snap ที่ฝั่งใดฝั่งหนึ่งของ Recycler View ก็ให้ใช้ GravitySnapHelper ของ Rúben Sousa ได้เลยจ้า ไม่ต้องไปทำเองให้เสียเวลา แต่ถ้าอยากให้แสดงผลคล้ายๆกับ View Pager ก็ใช้ PagerSnapHelper ได้เลย

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

        • SnapHelper [Android Developer]
        • RecyclerView snapping with SnapHelper [Rúben Sousa]
        • RecyclerViewSnap [GitHub]