มาทำ RecyclerView ใน Android บน Kotlin กันเถอะ
ปฏิเสธไม่ได้เลยว่า ทุกแอปนั้น ใช้เจ้า RecyclerView เพื่อแสดง data จาก API
เราควรอัพเดตบล็อกที่เขียนไว้ปีก่อนเขียนบล็อก จากโค้ด Java เป็น Kotlin เนอะ
ซึ่งฝั่ง 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'
สรุปไหนๆก็เป็น 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 นะเออ
ทำ 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 แบบง่ายจ้า
แต่ความโชคร้าย คือ มัน 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 เอาไปศึกษาต่อได้นะ ทำเพื่อการศึกษาล้วนๆ
สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ