08 พฤศจิกายน 2559

[Android Code] รู้จักกับ SnapHelper ของเล่นใหม่ที่เพิ่มเข้ามาใน Recycler View [เวอร์ชัน 24.2.0 ขึ้นไป]



        รู้สึกว่า Recycler View ในเวอร์ชัน 24.2.0 จะมีของเล่นใหม่ๆให้ใช้งานเยอะเหลือเกินนะ.. แต่ก็ถือว่าเป็นเรื่องดีแหละที่นักพัฒนาจะได้สะดวกสบายมากขึ้นจากการใช้งาน 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]

        ก็ให้ดาวน์โหลดมาแล้ว Imoport เข้ามาในโปรเจคของผู้ที่หลงเข้ามาอ่านได้เลย



        ถ้า Build Gradle แล้วมีปัญหาที่ build.gradle ของ GravitySnapHelper ก็ให้ไปลบคำสั่งทั้งสองนี้ออก เนื่องจากไม่ได้ใช้งาน

build.gradle
...

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
apply from: rootProject.file('gradle/gradle-bintray-upload.gradle')

        สำหรับการใช้งาน 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 ที่ฝั่งซ้ายของหน้าจอแล้วจ้า


สรุป

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

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

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

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




เหล่าพันธมิตรแอนดรอยด์

Devahoy Layer Net NuuNeoI The Cheese Factory Somkiat CC Mart Routine Artit-K Arnondora Kamonway Try to be android developer Oatrice Benz Nest Studios Kotchaphan@Medium Jirawatee@Medium Travispea@Medium