Fragment ตอนที่ 2 - Get Started
หลังจากที่เกริ่นไปคร่าว ๆ เกี่ยวกับ Fragment ในบทความตอนที่แล้ว คราวนี้มาดูวิธีการใช้งาน Fragment เบื้องต้นกันต่อดีกว่า
บทความในซีรีย์เดียวกัน
- Fragment ตอนที่ 1 - Introduction
- Fragment ตอนที่ 2 - Get Started [Now Reading]
- Fragment ตอนที่ 3 - Fragment Creation
- รู้จักกับ FragmentTransaction สำหรับการแสดง Fragment [ตอนที่ 1]
- รู้จักกับ FragmentTransaction สำหรับการแสดง Fragment [ตอนที่ 2]
- Lifecycle ของ Fragment (Fragment Lifecycle)
- วิธีการรับส่งข้อมูลของ Fragment
- มาทำ View Pager กันเถิดพี่น้อง~ [ตอนที่ 1]
- มาทำ View Pager กันเถิดพี่น้อง~ [ตอนที่ 2]
- เพิ่มลูกเล่นให้กับ View Pager ด้วย Page Transformer
อย่างที่บอกไปก่อนหน้านี้ว่านักพัฒนาแอนดรอยด์จะใช้งาน Fragment ที่อยู่ใน androidx.fragment.app.Fragment
ของ Library ที่ชื่อว่า AndroidX Fragment
implementation "androidx.fragment:fragment:<latest_version>"
โดยปกติแล้ว AndroidX Fragment จะอยู่ใน AndroidX AppCompat ด้วย ซึ่งเป็น Library พื้นฐานที่จะได้พบเจอทุกครั้งเมื่อสร้างโปรเจคขึ้นมาใหม่
แต่เพื่อให้ใช้งาน Fragment ได้ง่ายขึ้น เจ้าของบล็อกขอแนะนำให้เพิ่ม Dependency ของ Android KTX สำหรับ Fragment เข้าไปด้วย
implementation "androidx.fragment:fragment-ktx:<latest_version>"
Library ดังกล่าวจะเป็น Extension สำหรับการใช้งาน Fragment ในภาษา Kotlin ซึ่งจะมี Extension ต่าง ๆ ที่ทำให้นักพัฒนาเขียนโค้ดได้ง่ายขึ้นและสั้นกว่าเดิม
หรือจะเพิ่มแค่ Android KTX ของ Fragment ก็ได้ เพราะข้างในนั้นก็จะมี AndroidX Fragment อยู่แล้วเช่นกัน
สร้าง Fragment
ในการสร้าง Fragment จะสร้างคลาส Fragment ขึ้นมาเองตั้งแต่แรกหรือใช้เครื่องมือใน Android Studio ก็ได้เช่นกัน
ในกณีที่ต้องการสร้างผ่านเครื่องมือของ Android Studio ก็สามารถคลิกขวาที่ Package Name ที่ต้องการสร้าง Fragment แล้วเลือก New > Fragment > Fragment (Blank)
ได้เลย
ในระหว่างนี้จะเห็นว่าแถบเมนูของ Fragment จะมีให้เลือกสร้าง Fragment ในรูปแบบต่าง ๆ เพื่อช่วยลดระยะเวลาในการเขียนโค้ดที่ไม่จำเป็นให้น้อยลง แต่สำหรับการเรียนรู้เกี่ยวกับ Fragment ในเบื้องต้น เจ้าของบล็อกขอแนะนำให้เลือกเป็น Fragment (Blank) เพื่อให้มีแต่โค้ดที่จำเป็นต้องการเริ่มต้นเท่านั้น
Fragment ก็เป็น Component ตัวหนึ่งเหมือนกับ Activity ที่ใช้งานควบคู่กับ Layout Resource ในกรณีที่สร้างผ่าน Android Studio ก็จะสร้าง Layout Resource คู่กับ Fragment ให้โดยอัตโนมัติ แต่ถ้าสร้างไฟล์เองทั้งหมดก็อย่าลืมสร้าง Layout Resource ด้วยล่ะ
ถ้าตัดเรื่องของ UI ออกไป การสร้าง Fragment ในรูปแบบเริ่มต้นที่ง่ายต่อการทำความเข้าใจที่สุดจะเป็นแบบนี้
// HomeFragment.kt
class HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
companion object {
fun newInstance() = HomeFragment()
}
}
จากโค้ดตัวอย่างดังกล่าว มีสิ่งที่นักพัฒนาควรรู้อยู่ 5 เรื่องด้วยกัน
เรื่องที่ 1 - เจ้าของบล็อกสร้าง Fragment ชื่อว่า HomeFragment.kt
และสร้าง Layout Resource ชื่อว่า fragment_home.xml
เพื่อใช้งานร่วมกัน
เรื่องที่ 2 - เจ้าของบล็อกใช้ View Binding เพื่อช่วยให้เรียกใช้งาน View ได้สะดวก จึงมี FragmentHomeBinding
เพื่อทำ Layout Inflation สำหรับ View Binding เพิ่มเข้ามาด้วย (แนะนำ)
เรื่องที่ 3 - ถ้าสร้าง Fragment ผ่าน Android Studio โดยเลือก Fragment (Blank) จะพบว่ามีโค้ดอื่น ๆ ที่ถูกเตรียมไว้ให้บางส่วน ทำให้โค้ดดูเยอะกว่าโค้ดในตัวอย่างข้างบน
เรื่องที่ 4 - Fragment จะมี Lifecycle เช่นเดียวกับ Activity แต่มี Event เยอะกว่าและลำดับในการทำงานที่ยิบย่อยมากกว่า และหนึ่งในนั้นคือ onCreateView
นั่นเอง ซึ่งจะพูดถึงอีกทีในบทความ Let’s Fragment — Lifecycle ของ Fragment
แก้ Link ของบทความนี้ด้วย
เรื่องที่ 5 - การทำ Fragment ไปใช้งานจะนิยมสร้างเป็น Method ไว้ใน Companion Object ว่า newInstance()
แล้วให้เรียกใช้งานผ่าน Method ดังกล่าวแทน ซึ่งเป็นหนึ่งใน Best Practice ที่ควรทำ และจะมีพูดถึงอีกทีในบทความ Fragment ตอนที่ 3 - Fragment Creation
ไม่ต้องประกาศ Fragment ไว้ใน Android Manifest หรอ?
ใช่ครับ นักพัฒนาไม่จำเป็นต้องประกาศ Fragment ไว้ใน Android Manifest แต่อย่างใด
นั่นก็เพราะว่า Fragment ไม่ใช่ App Component อย่าง Activity ที่สามารถถูกเรียกใช้งานจากระบบแอนดรอยด์หรือแอปภายนอกได้ โดย Fragment จะถูกเรียกใช้งานจาก Activity หรือ Fragment ที่อยู่ในแอปเดียวกันเท่านั้น หรือก็คือแอปภายนอกจะเรียกใช้งานโดยตรงไม่ได้นั่นเอง
การใช้งาน Fragment ในรูปแบบต่าง ๆ
อย่างที่เคยบอกไปก่อนหน้านี้ว่า Fragment ไม่สามารถทำงานได้ด้วยตัวเอง ต้องทำงานอยู่บน Activity เสมอ ดังนั้นการนำ Fragment ใด ๆ ไปใช้งานจึงขาด Activity ไปไม่ได้เลย
ในขณะเดียวกัน Fragment ก็สามารถอยู่บน Fragment ได้เช่นกัน แต่ Fragment ที่เป็น Root Parent ก็จะต้องอยู่บน Activity อยู่ดี
และนอกจากนี้ Fragment สามารถอยู่บน Activity หรือ Fragment มากกว่าหนึ่งตัวก็ได้ ไม่จำเป็นต้องมีแค่ตัวเดียวเสมอไป (ขึ้นอยู่กับการออกแบบของนักพัฒนาเลย)
รู้จักกับ Fragment Manager
ในการจัดการกับ Fragment เช่น เพิ่ม Fragment เข้าไปใน FragmentContainerView หรือสั่งให้ Pop Back Stack ของ Fragment (หลักการเดียวกับ Back Stack ของ Activity) จะต้องทำผ่าน Fragment Manager เท่านั้น
และในปัจจุบันนักพัฒนาสามารถใช้ Jetpack Navigation เพื่อช่วยจัดการกับ Fragment แทน Fragment Manager ได้เช่นกัน (แต่ในบทความนี้จะยังไม่พูดถึง)
การเพิ่ม Fragment ให้กับ Activity หรือ Fragment ด้วยกัน
ในการเพิ่ม Fragment เข้าไปใน Activity หรือ Fragment จะมีอยู่ 2 วิธีด้วยกัน
- กำหนดไว้ใน Layout Resource ด้วย
<FragmentContainerView>
โดยตรง - กำหนดผ่าน Fragment Manager ควบคู่กับ
<FragmentContainerView>
บทความนี้จะยังไม่พูดถึงการใช้ Fragment กับ Jetpack Navigation
กำหนดไว้ใน Layout Resource ด้วย <FragmentContainerView>
โดยตรง
นักพัฒนาสามารถเพิ่ม <FragmentContainerView>
ไว้ใน Layout Resource และกำหนด Fragment ที่ต้องการแบบนี้ได้เลย
<androidx.fragment.app.FragmentContainerView
android:id="@+id/homeFragment"
android:name="com.akexorcist.fragment.basic.HomeFragment"
android:layout_width="..."
android:layout_height="..."
tools:layout="@layout/fragment_home" />
โดยวิธีนี้จะมีสิ่งสำคัญอยู่ 3 อย่างด้วยกัน
android:id
- ต้องกำหนด View ID ด้วยเสมอandroid:name
- กำหนดเป็นชื่อคลาสของ Fragment ที่ต้องการtools:layout
- เนื่องจาก Layout Preview ดึง UI ของ Fragment มาแสดงโดยตรงไม่ได้ จึงต้องกำหนด Layout Resource ของ Fragment ตัวนั้นเพื่อให้แสดงใน Layout Preview ได้
ใช้ Fragment Manager คู่กับ <FragmentContainerView>
แทนที่จะกำหนด Fragment ลงใน FragmentContainerView โดยตรง ก็เปลี่ยนมากำหนดผ่าน Fragment Manager แทน ดังนั้นการสร้าง FragmentContainerView ในวิธีนี้จึงไม่จำเป็นต้องกำหนดคลาส Fragment หรือ UI สำหรับ Layout Preview
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragmentContainer"
android:layout_width="..."
android:layout_height="..." />
เพราะ Fragment ที่จะเพิ่มลงใน FragmentContainerView จะกำหนดผ่าน Fragment Manager แทน โดยที่ Fragment Manager จะมี 2 แบบเช่นเดียวกับ Fragment และตัวที่จะใช้ก็คือ androidx.fragment.app.FragmentManager
ที่เป็น Fragment Manager ของ AndroidX Fragment นั่นเอง
แต่ในความเป็นจริง นักพัฒนาไม่ต้องสร้าง Fragment Manager ขึ้นมาเอง เพราะไม่ว่าจะเป็น Activity หรือ Fragment ก็ตาม จะมี Fragment Manager ให้เรียกใช้งานเสมอ
// Activity
val activity: Activity = /* ... */
val fragmentManager: FragmentManager = activity.supportFragmentManager
// Fragment
val fragment: Fragment = /* ... */
val fragmentManager: FragmentManager = fragment.childFragmentManager
val fragmentManager: FragmentManager = fragment.parentFragmentManager
childFragmentManager
กับparentFragmentManager
จะมีจุดประสงค์ในการใช้งานต่างกัน ดังนั้น ณ ตอนนี้ให้ใช้เป็นchildFragmentManager
ไปก่อน
และการเพิ่ม Fragment ด้วย Fragment Manager จะใช้คำสั่งแบบนี้
val fragmentManager: FragmentManager = /* ... */
fragmentManager.commit {
replace(R.id.fragmentContainer, HomeFragment.newInstance())
addToBackStack(null)
}
ถ้าใช้ View Binding แล้วอยากจะกำหนด View ID แบบนี้แทน ก็ทำได้เช่นกัน
val binding: HomeFragmentBinding = /* ... */
val fragmentManager: FragmentManager = /* ... */
fragmentManager.commit {
replace(binding.fragmentContainer.id, HomeFragment.newInstance())
addToBackStack(null)
}
สำหรับวิธีนี้จะมีสิ่งสำคัญอยู่ 3 อย่างด้วยกัน
commit { ... }
- เป็น Extension Function ของ Android KTX สำหรับ Fragment แทนการพิมพ์คำสั่งที่ยาวกว่านี้ (ใช้เถอะ)replace
- กำหนดให้ Fragment Manager เพิ่ม Fragment แทนที่ของเดิมที่อยู่ใน FragmentContainerView (ถ้าไม่มี Fragment ตั้งแต่แรก ก็จะทำให้ Fragment ตัวที่เราเพิ่มเข้าไปเป็นตัวแรกสุด)
เราสามารถเพิ่ม Fragment ไว้ใน View Group ใด ๆ ก็ได้ แต่การใช้ FragmentContainerView เป็น Best Practice ที่ทีมแอนดรอยด์แนะนำ
addToBackStack
- กำหนดให้เพิ่ม Fragment เข้าไปใน Back Stack ของ Fragment Manager (ถ้าไม่ต้องการให้อยู่ใน Back Stack ก็ไม่ต้องใส่)
สามารถดูรายละเอียดเกี่ยวกับ Back Stack ของ Fragment ได้ในบทความ Task และ Back Stack ตอนที่ 2 - Back Stack
ใช้วิธีไหนดีกว่ากัน?
ในการเพิ่ม Fragment ลงใน FragmentContainerView จะใช้วิธีไหนก็ได้ โดยขึ้นอยู่กับความต้องการของนักพัฒนาว่าอยากจะให้ Fragment ทำงานในลักษณะแบบไหน
การกำหนด Fragment ไว้ใน FragmentContainerView โดยตรงจะเหมาะกับกรณีที่นักพัฒนารู้อยู่แล้วว่า Fragment ตัวไหนจะเป็นตัวแรกสุดในนั้น
ส่วนการใช้ Fragment Manager จะมีข้อดีคือสามารถเพิ่ม Fragment ตัวใหม่ ๆ เข้าไปใน FragmentContainerView ได้เรื่อย ๆ (จึงมีคำสั่ง addToBackStack
สำหรับกำหนด Back Stack ของ Fragment ใน Fragment Manager) โดยการใช้คำสั่งอย่าง replace
ก็เป็นแค่หนึ่งในการใช้งานโดยทั่วไปเท่านั้น ส่วนคำสั่งอื่นที่เกี่ยวข้องจะพูดถึงในภายหลัง
สรุป
เพื่อพัฒนาแอปที่มีรูปแบบการทำงานที่ซับซ้อนได้ง่าย การใช้ Fragment จึงเป็นเรื่องสำคัญ ถึงแม้ว่า Fragment จะมีการทำงานที่ซับซ้อนมากกว่า Activity ก็ตาม แต่การสร้าง Fragment เพื่อใช้งานในเบื้องต้นก็ไม่ได้ซับซ้อนแต่อย่างใด
นอกจากนี้ก็ยังมีเรื่องอื่น ๆ ของ Fragment ที่นักพัฒนาควรรู้เพิ่มเติมเพื่อใช้งาน Fragment ได้อย่างมีประสิทธิภาพมากขึ้น