Android Kotlin

เขียนแอพฯ Android ด้วย Kotlin การใช้ RecyclerView และ Array

ตัวอย่างการพัฒนาแอพฯ บน 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)  
   }  
 }

ทดสอบแอพฯ ของเรา

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)

Related Articles

Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน