มาทำ RecyclerView ใน Android บน Kotlin กันเถอะ

Android Oct 4, 2018

ปฏิเสธไม่ได้เลยว่า ทุกแอปนั้น ใช้เจ้า RecyclerView เพื่อแสดง data จาก API

เราควรอัพเดตบล็อกที่เขียนไว้ปีก่อนเขียนบล็อก จากโค้ด Java เป็น Kotlin เนอะ

เพราะอะไร ทำไมถึงใช้ RecyclerView แทน ListView
สวัสดีค่ะทุกท่าน สำหรับคนที่เป็นสาย android dev แรกๆจะทำ custom listview เพื่อการแสดงผลในรูปแบบเดียวกัน

ซึ่งฝั่ง Kotlin โค้ดจะสั้นลง และสามารถ maintenance ง่ายขึ้น ด้วย KotlinX

ในวันนี้เราจะลองทำ RecyclerView ของ BNK48 กัน โดยแสดงรูป ชื่อ ของทั้งสองรุ่นเลย เย่เย้ ซึ่งเรามีหน้าตาที่เรา design ไว้คร่าวๆ นั่นคือ เป็น grid view และแยกสีรุ่น 1 และรุ่น 2

แน่นอนว่ายังมีความลังเลใจในการใช้ library สำหรับ RecyclerView นะ เพราะอาจจะใช้เจ้า Android Support Library ตัวเดิมก็ได้

implementation 'com.android.support:recyclerview-v7:28.0.0-beta1'

หรือจะลองของใหม่ AndroidX เพิ่งออก 1.0.0 เมื่อวันที่ 21 กันยายน ที่ผ่านมา เย้ เรียกได้ว่า เขียนบล็อกหลังจากออกตัวเต็มได้ 2 วันเลยแหละ

implementation 'androidx.recyclerview:recyclerview:1.0.0'

AndroidX release notes | Android Developers
This release candidate of AndroidX is considered feature-complete and its public API surface is stable. This release…developer.android.com

สรุปไหนๆก็เป็น Tutorial Blog แล้ว ใช้ของใหม่แล้วกันเนอะ 5555555

ไปเพิ่ม library RecyclerView ของ AndroidX

เริ่มแรกไปที่ build.gradle ของแอป ใส่เจ้า library ของ AndroidX นั่นแหละ ซึ่งข้อควรระวัง คือ อันที่เคยเป็น Android Support Library แก้ไปเป็น AndroidX ด้วยนะ แบบนี้

implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.recyclerview:recyclerview:1.0.0'

ซึ่งสามารถใช้ลิ้งค์ด้านล่างนี้ ในการประกอบการ refactor นะเออ

AndroidX refactoring | Android Developers
API packages which are not bundled with the Android operating system are being refactored into the new androidx…developer.android.com

ทำ Layout รอ

อันนี้ตามถนัดก่อน ฮ่าๆ เราจะเริ่มที่ layout ก่อนว่ามันมีอะไรบ้างนะ

หน้า activity ของเรานั้น จะมีเจ้า fragment วางอยู่ แล้วมี RecyclerView อยู่ในนั้น และแต่ละ item จะมีรูปเมม ชื่อ แสดงเป็น grid view แยกสีตามรุ่นไป พอกดแต่ละ item จะเป็นรายละเอียดของเมมแต่ละคน ซึ่งอันนี้ขอข้ามก่อนแล้วกันนะ ฮ่าๆ

ขอรวบ layout ทั้งหมด เป็นดังนี้

สีนั้นไปจิ้มที่แคปรูปหน้าเว็บ Official แล้วเข้าไปทำที่เว็บ https://coolors.co/ จ้า

สร้าง Model Class

หลังจากเราทำความเข้าใจและรู้ว่าอะไรต้องแสดงผลตอนไหน เราจะต้องทำ model class เพื่อรับ data มาจาก API ซึ่งไปเจอมาแหละ แต่ในที่นี้เราแอบโม data มา

ดังนั้นเราจึงขอ mock ไว้ใน json แล้วกันเนอะ และขอวาปข้ามในการสร้างเจ้านี่นะ

เราเอาไฟล์ json ไปวางไว้ใน assets ซึ่งมันจะอยู่ที่ <appname>/app/src/main/assets นะ

และการเจ้า json file ใน assets ไปใช้งานนั้น เริ่มจากดึงไฟล์นี้ขึ้นมาก่อน จากนั้นอ่านเป็น byte array ออกมา แปลงเป็น String ปกติมาตรฐาน แล้วแปลง type ออกมา จากนั้นเอาไปใช้งานต่อได้เลยจ้า

สร้าง Adapter ขึ้นมาซะดีๆ

ก่อนอื่นทบทวนเรื่อง RecyclerView กันสักนิดนึงนะ เรียนรู้จากประสบการณ์การ debug จริง

สมมุตินะว่าเรามี data ที่ต้องแสดงใน RecyclerView นี้ 50 อัน

RecyclerView สร้าง holder จาก OnCreateViewHolder ซึ่งในรูปมี 3 อันนิดๆ แล้วมีการสร้างเผื่อในนี้ด้วย เช่น สร้างไปถึงตำแหน่งที่ 10 ก็คือทำเท่าที่พอใช้เท่านั้น พอเราเลื่อนลงไปสักพักนึง เช่น อันที่ 5 มันก็จะสร้างของใหม่เพิ่มให้ครบ และเอาของเก่าทิ้งไปงี้ โดยคืนค่าเป็น ViewHolder แล้วโยนให้ system จัดการ แบบสวยๆ ไม่ต้องเหนื่อยมาก แล้วก็แล้ว set attribute ต่างๆ เช่น ใส่รูป ใส่ตัวหนังสือ ที่เจ้า onBindViewHolder ส่วนจำนวนทั้งหมดจริงๆคือ getItemCount

แต่ถ้าแปลงเป็น Kotlin นั้นแบบก็อปแปะแปลง พบว่ามันจะมีการใช้เจ้า findViewById หลงเหลืออยู่ในส่วนของเจ้า ViewHolder นะ ซึ่งตรงนี้สามารถใช้ KotlinX ช่วย ทำให้โค้ดดูสะอาดและอ่านง่ายขึ้น ซึ่งเราไม่มีโค้ดตัวอย่างตรงนี้ประกอบนะ ;_;

ไฟล์ที่ว่าคือเจ้า Adapter นั่นเองแหละจ้า parameter ขาเข้า คือ สิ่งที่ได้จาก json file นั่นเอง และใส่เจ้า adapter ลงไป ซึ่งเราจะสร้างแยก class ออกมา

อันนี้แบบคร่าวๆ เราจะเห็น class ที่เราต้อง override จาก RecyclerView.Adapter 3 ตัว คือ onCreateViewHolder, getItemCount และ onBindViewHolder โดยใน onBindViewHolder นั้น จะเป็นการ set attribute ต่างๆที่ ViewHolder และถ้าเราใส่ listener ของ view ต่างๆ สามารถใส่ event ได้ที่นี่เช่นกัน เช่น เราอยากให้กด item เพื่อไปหน้า detail ของเมมเบอร์ ซึ่งยังไม่ทำตอนนี้นะ

holder.itemView.setOnClickListener { listener.onItemClick() }

ดังนั้นเราจึงสร้าง class ViewHolder และสร้าง function bind เพื่อนำเจ้า item แต่ละตัว มา render เป็น view ที่ถูกใส่ค่า attribute ที่ได้มาจาก data ต่างๆ นั่นเอง

เจ้า itemView แต่ละตัวจะเชื่อมกับ ViewHolder ที่ได้จาก onCreateViewHolder ดังนั้นจึงใช้ KotlinX ในการระบุ id ของ view นั้นๆ เช่น เราจะใส่ชื่อเล่นของเมมแต่ละคน ซึ่งชื่อเล่นจะอยู่ใน member.nickname.en และ setText ที่ TextView ที่ชื่อว่า textMemberNickName

itemView.textMemberNickName.text = member.nickname.en

ในที่นี้เราจะแยกเมมทั้งสองรุ่นด้วยตัวแปร member.generation และใส่สีพื้นหลังให้ต่างกันซะ แบบนี้

เรามี trick นิดนึง ในกรณีที่มี view 2 อันที่แตกต่างกัน แบบใช้ xml คนละตัวกัน แต่ใส่ข้อมูลต่างๆเกือบจะเหมือนกันแบบจะสร้าง 2 class ที่คล้ายกันทำไม สามารถเอา type ที่เราโยนเข้า Adapter มาใช้ layout แยกกันที่ onCreateViewHolder นะ

ยังไม่จบซะทีเดียว เหลือแต่แปะรูปเมมเบอร์เนอะ อันนี้เป็นการใช้ Glide บน Kotlin แบบง่ายจ้า

Using Glide with Kotlin
Glide is the best image loading library for Android. On version 4, Glide received huge API changes that slightly changed how we can use it in our apps. To make it work with Kotlin, follow these…

แต่ความโชคร้าย คือ มัน build ไม่ผ่าน พอเจ้า Glide มันเป็น v4 ใช้ยุ่งยากมาก คนบ่นเยอะมากด้วย เลยขอใช้ Picasso แทนแปป

ไปจัดการ Fragment ที่มันมี RecyclerView อยู่

ก่อนอื่นไปที่ MainActivity แล้วแปะ MainFragment ตามท่ามาตรฐานซะ

savedInstanceState ?: supportFragmentManager.beginTransaction()
        .replace(R.id.container, MainFragment())
        .commit()

ไปที่เจ้า MainFragment ย้อนความจากข้างบนนิดนึง เราได้เจ้า ArrayList<Member> มาจากไฟล์ json ใช่ม่ะ และเราก็สร้าง class Adapter เอาไว้แล้ว เราจะมาเรียกใช้ไปพร้อมๆกับการใส่ adapter ใน RecyclerView

การแสดงผลในที่นี้ คือ แสดงผลเป็น Grid มีแถวละ 2 items ดังนั้นเรา set แบบนี้นะ

recyclerView.layoutManager = GridLayoutManager(context, 
                                 2, //จำนวนแถว
                                 GridLayoutManager.VERTICAL, //แนว
                                 false)

ถ้าอยากทำแบบ List แบบยาวๆ ก็สามารถทำได้เช่นกัน แบบนี้

recyclerView.layoutManager = LinearLayoutManager(context,
                                LinearLayoutManager.VERTICAL, //แนว
                                false)

ผลสุดท้ายเป็นแบบนี้นะ

เป็นอันจบการเขียนบล็อก RecyclerView ด้วย Kotlin แด่เพียงเท่านี้

และแน่นอนว่า ไหนๆสร้างโปรเจกมาขนาดนี้แล้ว จะไม่จบแค่นี้แน่นอนจ้า ติดตามกันต่อไปต่อไป ต่อไปต่อให้ได้ แม่นํ้ากว้างใหญ่โฮะๆ เดี๊ยวววววว อันนี้ใน github เอาไปศึกษาต่อได้นะ ทำเพื่อการศึกษาล้วนๆ

mikkipastel/BNK48member
Sample for RecyclerView from AndroidX with Kotlin. Contribute to mikkipastel/BNK48member development by creating an account on GitHub.

สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ

อย่าลืมกด like กด share บทความกันด้วยนะคะ :)

Posted by MikkiPastel on Sunday, 10 December 2017

Tags

Minseo Chayabanjonglerd

I am a full-time Android Developer and part-time contributor with developer community and web3 world, who believe people have hard skills and soft skills to up-skill to da moon.