ตัวอย่างการพัฒนาแอพฯ บน Android ด้วยภาษา Kotlin กับการเรียกใช้ RecyclerView จัดการ Array Adapter อย่างง่าย
สำหรับตัวอย่างนี้จะใช้ RecyclerView ที่น่าจะมาแทน Widget อย่าง ListView หรือ GridView กันแล้ว อีกทั้งเจ้า RecyclerView เองก็มีคุณสมบัติของการใช้ GridLayoutManager ร่วมกับตัวมันเอง ส่วนหลักการของ Binding data นั้นก็ใช้การแปลง Array เป็น Adapter แล้วเข้าไปยัดลง Widget ของ RecyclerView ได้ตรงๆ โดยอาศัย ViewHolder มาแสดงผลแถวแต่ละแถวอีกที
สร้าง New Project ขึ้นมาใหม่เป็น Empty Activity อย่าลืมเลือก Include Kotlin support เพื่อทำให้ Project นี้ใช้ภาษาโปรแกรม Kotlin เป็นหลัก
ระหว่างที่ทำการรอการใช้งานของ Android Studio ของเราอยากให้ทุกคนไปดาวน์โหลดภาพเหล่านี้จาก URL: https://drive.google.com/drive/folders/0B08PZSOd4UmOOXJjaGVESFg0Skk
ทำการวางภาพทั้งหมดลงใน folder ที่ app->res->drawable ดังภาพตัวอย่าง:
ไปที่ activity_main.xml หน้าจอออกแบบ Layout ให้สังเกตส่วนของ Pallete ส่วนของ Container ทำการคลิกดาวน์โหลด Widget ของ RecyclerView และ CardView ให้เรียบร้อย:
ออกแบบ xml ของ activity_main.xml ดังนี้:
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toBottomOf="parent" android:id="@+id/recyclerView"/> </android.support.constraint.ConstraintLayout>
ตั้งชื่อ ID ให้เจ้า RecyclerView ว่า recyclerView หลังจากนั้นไปประกาศใน MainActivity.kt ดังนี้:
var recyclerView: RecyclerView? = null
ประกาศในส่วนของ Global Variable ตามด้วยตัวแปรของ Array ที่จะเป็น Array ของ String ในตัวแปรชื่อ foods และ ตัวแปรของ Int ที่เก็บ index ของรูปภาพใน Drawable
var foods = arrayOf( "Minced pork omelette", "Stir fried pork with basil", "Papaya salad", "Boiled egg, Bael leaves", "Pad Thai", "Korat Noodle", "Shrimp Salad", "Boiled pork noodles", "Steamed sea", "Steamed rices", "Pork Belly", "Liang Vegetable Fried Eggs" ) var arrImg = arrayOf<Int>( R.drawable.image1, R.drawable.image2, R.drawable.image3, R.drawable.image4, R.drawable.image5, R.drawable.image6, R.drawable.image7, R.drawable.image8, R.drawable.image9, R.drawable.image10, R.drawable.image11, R.drawable.image12 )
หลังจากนั้นสร้าง Class ใหม่เป็น Kotlin Class ใน package name เดียวกับ MainActivity ชื่อว่า MyAdapter
ประกาศ Extend ของ MyAdapter.kt Class ดังนี้:
class MyAdapter(val items: Array<String>, val imageId: Array<Int>, val context: Context) : RecyclerView.Adapter<ViewHolder>() { }
เราจะพบว่ามี error เกิดขึ้น 2 จุดคือ class MyAdpater และ <ViewHolder> ให้ทำการ alt + enter ที่ class ก่อนเพื่อ Implement Memebers ของ Class นี้ให้มีเมธอดหลักของการจัดการ Adapter:
เราจะได้ Class ของ MyAdapter.kt ดังนี้:
class MyAdapter(val items: Array<String>, val imageId: Array<Int>, val context: Context) : RecyclerView.Adapter<ViewHolder>() { override fun onBindViewHolder(p0: RecyclerView.ViewHolder, p1: Int) { //TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun getItemCount(): Int { //TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } override fun onCreateViewHolder(p0: ViewGroup, p1: Int): RecyclerView.ViewHolder { //TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }
หลักๆ คือเราจะมีการเรียก เมธอด MyAdapter โดยการส่ง Parameter มา 3 ค่าคือ foods, arrImg, this (หน้าปัจจุบัน) ไปยัง
MyAdapter(val items: Array<String>, val imageId: Array<Int>, val context: Context)
เราจึงจะต้องมีการ นำค่า Array Size ก่อนผ่าน ฟังก์ชัน getItemCount() ให้ return จำนวน Array
override fun getItemCount(): Int { return items.size }
ส่วนของจัดการ แถวของข้อมูลใน RecyclerView จะเป็นการใช้ ViewHolder มารองรับ เราจะสร้างหน้า Layout ใหม่ชื่อ model.xml มาวนในแต่ละแถวให้แก้ไขฟังก์ชัน onCreateViewHolder() ดังนี้:
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ViewHolder { return ViewHolder(LayoutInflater.from(context).inflate(R.layout.model, p0, false)) }
กด alt + enter เพื่อสร้าง model.xml
เราจะได้ไฟล์ใหม่ในโฟลเดอร์ “app->res->layout->model.xml”
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="170dp" android:layout_height="190dp" android:layout_marginLeft="20dp" android:layout_marginTop="10dp" android:orientation="horizontal" android:padding="5dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/thumbnail" android:layout_width="match_parent" android:layout_height="133dp" android:scaleType="centerCrop"/> <TextView android:id="@+id/nameTxt" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:textAlignment="center" android:textSize="16sp" /> </LinearLayout> </LinearLayout>
กลับไปที่ MyAdapter.kt ให้เราสร้าง Inner Class ในไฟล์นั้นต่อท้าย class ของ MyAdapter คือการสร้าง class ชื่อ ViewHolder ขึ้นมา
โดยมีหน้าที่คือ เรียกตัวแปร getNameTxt และ getThumbnail มารับค่า widget ID ใน model.xml
class ViewHolder (view: View) : RecyclerView.ViewHolder(view) { val getNameTxt = view.nameTxt val getThumbnail = view.thumbnail }
ขั้นตอนสุดท้ายคือ onBindViewHolder() ที่เป็นการ Mapp ค่า array ที่รับมากลายเป็น Adapter ยัดใส่ Widget แต่ละตัวใน ViewHolder
override fun onBindViewHolder(p0: ViewHolder, p1: Int) { p0?.getNameTxt?.text = items.get(p1) p0?.getThumbnail?.setImageResource(imageId.get(p1)) }
ดังนั้น class ของ MyAdapter.kt จะเป็นดังนี้:
class MyAdapter(val items: Array<String>, val imageId: Array<Int>, val context: Context) : RecyclerView.Adapter<ViewHolder>() { override fun onBindViewHolder(p0: RecyclerView.ViewHolder, p1: Int) { p0?.getNameTxt?.text = items.get(p1) p0?.getThumbnail?.setImageResource(imageId.get(p1)) } override fun getItemCount(): Int { return items.size } override fun onCreateViewHolder(p0: ViewGroup, p1: Int): RecyclerView.ViewHolder { return ViewHolder(LayoutInflater.from(context).inflate(R.layout.model, p0, false)) } } class ViewHolder (view: View) : RecyclerView.ViewHolder(view) { val getNameTxt = view.nameTxt val getThumbnail = view.thumbnail }
กลับมาที่ MainActivity.kt ให้เขียนคำสั่งในการ เรียกใช้ GridLayoutManager และเพิ่ม setAdapter เพิ่มเข้าไปใน onCreate() สำหรับ RecyclerView
recyclerView = findViewById(R.id.recyclerView) as RecyclerView recyclerView!!.layoutManager = LinearLayoutManager(this) recyclerView!!.setLayoutManager(GridLayoutManager(this, 2))
คำสั่งข้างต้นคือการทำ GridLayoutManager ของ RecyclerView เป็น 2 Columns ส่วนคำสั่งในการ setAdapter ข้อมูลใส่ใน RecyclerView คือ:
val myAdapter = MyAdapter(foods,arrImg,this) recyclerView!!.setAdapter(myAdapter)
ดังนั้นไฟล์ MainActivity.kt จะเป็นดังนี้:
package com.daydev.iapplication import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.support.v7.widget.GridLayoutManager class MainActivity : AppCompatActivity() { var recyclerView: RecyclerView? = null var foods = arrayOf( "Minced pork omelette", "Stir fried pork with basil", "Papaya salad", "Boiled egg, Bael leaves", "Pad Thai", "Korat Noodle", "Shrimp Salad", "Boiled pork noodles", "Steamed sea", "Steamed rices", "Pork Belly", "Liang Vegetable Fried Eggs" ) var arrImg = arrayOf<Int>( R.drawable.image1, R.drawable.image2, R.drawable.image3, R.drawable.image4, R.drawable.image5, R.drawable.image6, R.drawable.image7, R.drawable.image8, R.drawable.image9, R.drawable.image10, R.drawable.image11, R.drawable.image12 ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) recyclerView = findViewById(R.id.recyclerView) as RecyclerView recyclerView!!.layoutManager = LinearLayoutManager(this) recyclerView!!.setLayoutManager(GridLayoutManager(this, 2)) val myAdapter = MyAdapter(foods,arrImg,this) recyclerView!!.setAdapter(myAdapter) } }
ทดสอบแอพฯ ของเรา
One Comment