มาตีบวก Android Studio กัน — File template
คราวก่อนเคยมาเสนอ Feature แห่งความขี้เกียจของ Android Studio ไปแล้ว นั้นคือ Live Template — แต่ถ้าเราจะขี้เกียจแล้ว เราต้องขี้เกียจให้มากขึ้น Live Template ช่วยเรา Generate Code แค่บางส่วนเท่านั้น แต่เรามีวิธีที่ทำให้ Android Studio ช่วยเรา Generate Code ทั้งไฟล์เลย Feature นี้ชื่อว่า File Template
สำหรับใครยังไม่รู้จัก Live Template อ่านได้จาก Blog ข้างล่างจ้าาาา
จากตัวอย่างใน Blog ก่อน คือความขี้เกียจในการกำหนด View ให้กับ RecyclerView เลย สร้าง Live Template ซะช่วยให้เราเขียนโค้ดส่วนกำหนด View ง่ายขึ้น แน่นอนว่ามี List แล้ว ต้องมี Adapter ตามมา แต่ว่า Adapter มันคือ Class นึงเลย จะมานั้งเขียนเป็น Live Template ก็คงไม่เหมาะ เรามาทำ File Template เลยดีกว่า
ListAdapter ต้องการอะไรบ้าง?
การเขียน List Adapter เป็นอะไรที่มี Pattern ตายตัวมาก อาจจะมีต่างกันนิดหน่อยในแต่ละบริษัท (ที่กำหนดโค้ด Pattern ต่างกัน) แต่สุดท้าย List Adapter ต้องการของ 4 อย่างหลักๆ ดังนี้
1. Class List Adapter
แน่นอนว่ามันต้องมี Class ของมันก่อน และต้อง Extends RecyclerView.Adapter<RecyclerView.ViewHolder>
เสมอ จากนั้นต้อง implement method ที่จำเป็น
- onCreateViewHolder — ไว้สำหรับสร้าง ViewHolder
- getItemCount — เป็นตัวกำหนดจำนวนของ Items ใน List
- onBindViewHolder — มีไว้เพื่อ Bind data ของ View กับ Item เข้าด้วยกัน
- getItemId (Optional) — จะมีหรือไม่มีก็ได้ แต่แนะนำให้ใส่ เพราะ ID ของ Item ใน List มีประโยชน์มาก ไม่ว่าจะทำการเปลี่ยน State select/deselect การเชื่อมต่อข้อมูล การ Refersh data; ตัว ID จะช่วยทำให้ List แสดงผล Smooth และ Data ไม่ผิดไป
2. Items
Items ใน List ก็เป็นสิ่งจำเป็นใน Adater เพราะถ้าไม่มี Items จะเอาข้อมูลที่ไหนไปแสดงหล่ะ 5555
3. View Layout
ในแต่ละ Item ของ List ต้องการ Layout ในการแสดงผลด้วยนะ อย่าลืมสร้าง XML ของแต่ละ Item ด้วย
4. ViewHolder
เป็นสิ่งที่ขาดไม่ได้เป็นที่สุด เป็นส่วนที่จะทำให้ List ไม่กิน Memmory โดยการ Reuse View จาก ViewHolder
5. Item Binding (Optional)
ถ้าเกิดเรา Enable Data Binding ไว้ใน Project และสร้าง File XML โดยมี Parent เป็น <layout></layout>
Android Studio จะสร้าง File ฺItemBinding ไว้ให้ทำ Data Binding
Naming Convention
จากข้อมูลข้างต้อง จะเห็นว่าการสร้าง List Adapter อันเดียว มันต้องสร้าง File เยอะแยะมากกกกกกกกกก — ถ้าเราไม่จัดการเรื่อง Naming Convention ดีๆ ละก็ พัง!! พังแน่ๆ เราจะงงว่าอะไรเป็นอะไร ตัวอย่างใน Blog นี้จะใช้ Naming convention ดังนี้
1. Class List Adapter
{Name}ListAdapter.kt
2. Items
List<{Name}Item>
3. View Layout
R.layout.item_{name} — name ใน Snack Case
4. ViewHolder
{Name}ViewHolder
5. Item Binding (Optional)
File นี้จะถูกสร้างโดย Android Studio โดยนำชื่อไฟล์ .XML มาเขียนใน Camel Case และเพิ่มคำว่า Binding จะได้ Item{Name}Binind
จาก Naming Convention ข้างบน ถ้าเราอยากได้ List ของ HackerNews เราจะได้ File ดังนี้
- HackerNewsListAdapter.kt
- List<HackerNewsItem>
- R.layout.item_hacker_news
- HackerNewsViewHolder
- ItemHackerNewsBinding
Duplicate Code
จากที่กล่าวมาข้างต้น การสร้าง List Adapter จะเป็นการเขียนโค้ดซ้ำๆ ควรเอา Adapter หลายๆ Class มาวิเคราะห์ว่าส่วนไหนซ้ำๆ กันบ้าง ส่วนไหนที่ต้อง Dynamic จากนั้นเราสามารเพิ่ม File Template ได้
จากรูปโค้ดข้างบนส่วนที่อยู่ในกรอบสีฟ้า คือส่วนที่ Dynamic ที่เราต้องการจะเปลี่ยนเวลาสร้าง Tempalte
เกริ่นมาเยอะ เรามาเริ่มสร้าง File Template กันเถอะ!!!
สร้าง File Template
- ไปที่เมนู File →New →Edit Template
จากนั้น Android Studio จะแสดง Form สำหรับแก้ไขและสร้าง Template
2. กด +
เพื่อเพิ่ม File template
จากนั้นเราจะเจอ Form แบบรูปข้างบนมีช่อง 3 ช่องให้กรอก
Name — ให้ใส่ชื่อของ Template ไม่ใช่ชื่อไฟล์ที่จะสร้างนะ
Extension — ใส่นามสกุลไฟล์ลงไป ในที่นี้คือ kt
ช่องใหญ่ๆ — คือ Code Template ที่เราจะใส่
ใน Template Form จะมีตัวแปรที่ถูกกำหนดไว้แล้วบางส่วน สามารถอ่านได้ใน description ของ form
3. ใส่ Code Template ลงไป โดยเราสามารถใช้ Syntax ${VARIABLE_NAME}
ในการกำหนดส่วนที่ต้องการให้ Dynamic โดยส่วนที่ Dynamic สามารถดูได้ในหัวข้อ Duplicate Code
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#endimport android.databinding.DataBindingUtil
import android.support.v4.app.FragmentActivity
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import news.ta.com.news.R
import news.ta.com.news.databinding.${ITEM_BINDING_CLASS}class ${NAME} : RecyclerView.Adapter<${NAME}.${VIEWHOLDER_CLASS}>() {var items: List<${ITEM_CLASS}> = emptyList()
set(value) {
field = value
notifyDataSetChanged()
}init {
setHasStableIds(true)
}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ${VIEWHOLDER_CLASS} {
val view = LayoutInflater.from(parent.context).inflate(${LAYOUT_RESOURCE}, parent, false)
return ${VIEWHOLDER_CLASS}(view)
}override fun getItemId(position: Int): Long {
return items[position].id.toLong()
}override fun getItemCount() = items.sizeoverride fun onBindViewHolder(holder: ${VIEWHOLDER_CLASS}, position: Int) {
holder.bind(items[position])
}inner class ${VIEWHOLDER_CLASS}(itemView: View?) : RecyclerView.ViewHolder(itemView) {
val binding: ${ITEM_BINDING_CLASS}? = itemView?.let { DataBindingUtil.bind(it) }fun bind(item: ${ITEM_CLASS}) {
binding?.item = item
}
}
}
ที่สำคัญอย่าลืมส่วน import นะ ไม่งั้นต้องมาตามแก้ import กันอีก
4. กด OK
จากนั้นเราจะได้ File Template ที่ต้องการ
การเรียกใช้งาน
- ไปยัง Package ที่ต้องการ กดคลิกขวา เลือก New → ชื่อ Template ของเรา ในที่นี้ชื่อ List Adapter
2. จากนั้น Android Studio จะแสดง form มาให้เราใส่ข้อมูล ข้อมูลต่างๆ มาจากตัวแปรที่เราใส่ไปใน Template นั้นแหละ — ณ จุดๆ นี้ ถ้าเรามี Naming Convension ในการสร้างไฟล์ มันจะทำให้เรากรอกข้อมูลตรงนี้ได้ไวมากกกก — ว่าแล้วก็ใส่ข้อมูลเลย
3. กด OK
จากนั้น Android Studio จะสร้างไฟล์ใหม่ให้เรา พร้อมกับโค้ดทั้งก้อน อย่างสวยงาม
เสร็จแล้ว — สุด!! ไปเลย ใครจะรู้ว่ามันจะมีฟีเจอร์สนองความขี้เกียจขนาดนี้ อย่าลืมใช้กันเยอะๆ นะ
Beyond ความขี้เกียจ
เมื่อเราได้ File Template แล้ว ตอนเรียกใช้งานมันต้องการตั้ง 5 ข้อมูลเลย และจาก Convention ที่เรากำหนดไว้ว่า มันคือ Adapter ของ HackerNews แล้วจะจะสร้าง Template ให้ โดยที่ File ต่างๆ ส่วยแต่มีคำว่า HackerNews อยู่ทั้งนั้น ทำไม เราไม่ทำ Template ที่กรอกแค่ ชื่อไฟล์,
Data ของ Adapter
และ Layout
หล่ะ — เราทำได้นะ แต่ Convention เราต้องแน่นมาก — งั้นเราไปแก้ Template กัน
ไปที่ File →New →Edit Template → Template ที่เราสร้าง
แก้โค้ดตามนี้
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#endimport android.databinding.DataBindingUtil
import android.support.v4.app.FragmentActivity
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import news.ta.com.news.R
import news.ta.com.news.databinding.Item${DATA_ADAPTER}Bindingclass ${DATA_ADAPTER}ListAdapter : RecyclerView.Adapter<${DATA_ADAPTER}ListAdapter.${DATA_ADAPTER}ViewHolder>() {var items: List<${DATA_ADAPTER}Item> = emptyList()
set(value) {
field = value
notifyDataSetChanged()
}init {
setHasStableIds(true)
}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ${DATA_ADAPTER}ViewHolder{
val view = LayoutInflater.from(parent.context).inflate(R.layout.${LAYOUT_ID}, parent, false)
return ${DATA_ADAPTER}ViewHolder(view)
}override fun getItemId(position: Int): Long {
return items[position].id.toLong()
}override fun getItemCount() = items.sizeoverride fun onBindViewHolder(holder: ${DATA_ADAPTER}ViewHolder, position: Int) {
holder.bind(items[position])
}inner class ${DATA_ADAPTER}ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
val binding: Item${DATA_ADAPTER}Binding? = itemView?.let { DataBindingUtil.bind(it) }fun bind(item: ${DATA_ADAPTER}Item) {
binding?.item = item
}
}
}
จากโค้ดข้างบนมีตัวแปรแค่ 2 ตัวคือ ${DATA_ADAPTER}
และ ${LAYOUT_ID}
ซึ่งต่างกับโค้ดก่อนหน้าที่มีตัวแปรถึง 4 ตัว — ส่วน File name เป็นช่องบังคับใส่อยู่แล้ว
การเรียกใช้งานจะได้ดังรูป
ขี้เกียจได้อีก
สรุป
Android Studio มี Feature ที่สนองความขี้เกียจอีกเยอะมาก ควรใช้ให้ถูกจังหวะนะ ที่สำคัญในบทความนี้ที่อยากจะเน้นย้ำคือเรื่อง Naming Convention ต่อให้ Android Studio ช่วยเราได้มากแค่ไหน แต่ถ้าเราไม่ตั้ง Naming Convention ดีๆ มันก็ยากที่จะทำให้โค้ด Maintain ได้ — File Template ที่ต้องทำงานร่วมกันหลายๆ class ถ้าเราอยากให้มันอ่านออกหลังจากสร้างเสร็จอย่าลืมเรื่อง Name Covnetion ด้วยนะ
File Template ที่ดี ควรเริ่มจากโค้ดมี Pattern ค่อนข้างตายตัว และมี Convention ที่ตกลงระหว่างทีมที่ชัดเจน มันจะทำให้เราสร้าง File Template ที่ชัดเจน และเป็นมิตรกับ Developer มากขึ้น — อย่าใช้ Feature นี้เพราะความขี้เกียจอย่างเดียว ใช้ให้ดีด้วย — อย่าลืมกลับไปดูโค้ดตัวเองกัน ว่ามีส่วนไหนที่ต้องทำ File Template มั้ย ถ้ามีก็เริ่มทำเลย จะได้ไม่เป็นภาระกับ Developer คนถัดไป
ขอบคุณที่อ่านจนจบจ้าาา
— จบ —