Android Navigation Component คืออะไรน้าา? — Part.1
คืออะไรน้าา?
ก็คือ Component นั่นเอง ที่อยู่ในหมวด Architecture ของ Android Jetpack เปิดตัวขึ้นในงาน Google I/O 2018 ที่ผ่านมา
Navigation มีความโดดเด่นในเรื่องการจัดการ flow ของ app ในการ navigate ไปยัง screen ต่างๆ ซึ่งมีลักษณะคล้ายกับ Storyboard ใน XCode ของ iOS เลยค่ะ
ใช้ได้ที่ Android Studio 3.3 ขึ้นไป
CONCEPT = Single Activity More Fragment
แล้วดียังไงล่ะ?
- เห็นภาพ flow ของ app ว่าแต่ละ screen มีการ navigate ไปไหนบ้าง
- ช่วยจัดการ fragment transaction ให้เอง (back stack ordering)
- ทำ animation และ transition ได้
- Passing data โดย Safe Args
- Deeplink
Navigation Component ประกอบไปด้วย
- Navigation Graph คือ XML resource file ทำหน้าที่เชื่อมต่อ NavHostFragment กับ Destination (Screen ต่างๆ) เข้าด้วยกัน รวมถึงการกำหนด action ให้กับ Destination
- NavHostFragment เป็น Container ที่เอาไว้แสดง Destination โดยใส่ไว้ใน Layout ของ Activity
- NavController ไว้จัดการเรื่อง navigate
สำหรับบทความนี้จะพาไปทำ Navigation แบบเบื้องต้นกันค่ะ
โจทย์คือ
มี MainActivity ไว้วาง MainFragment และMainFragment มีปุ่มกดเพื่อเปิด AboutFragment ตามรูป
Getting Started
เพิ่ม dependencies ใน build.gradle(app) ดังนี้
จากนั้นสร้าง Navigation Graph โดยคลิกขวาที่โฟลเดอร์ res เลือก New > Android Resource File แล้วเลือก Resource type: Navigation และตั้งชื่อไฟล์
จะได้ไฟล์ nav_graph_main.xml ในโฟลเดอร์ navigation ตามรูป
หน้าตาของ navigation graph เป็นดังนี้ ยังไม่มี screen ใดๆเลย
จากนั้น implement NavHost ที่ main_activity.xml ตามนี้
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"> <fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph_main" /></androidx.constraintlayout.widget.ConstraintLayout>
- android:name=“androidx.navigation.fragment.NavHostFragment”
- app:defaultNavHost ใส่ค่า boolean ถ้าใส่ true คือ กำหนดให้ NavHost นั้นจัดการกับปุ่ม back ให้เรา
- app:navGraph ใส่ id ของ Navigation Graph (ไม่ใช่ชื่อไฟล์นะ อาจจะสับสนได้เพราะ id กับชื่อไฟล์เหมือนกัน)
ทำความรู้จัก Navigation Graph กันก่อนค่ะ
- Destinations แสดง Host ที่ Navigation Graph นี้เชื่อมต่ออยู่ และ destination ที่ใช้ทั้งหมด
- Editor
- Attributes
- ปุ่ม New Destination ไว้เพิ่ม destination ที่ต้องการ
** Destination คือ activity หรือ fragment นั่นเอง **
<fragment
android:id="@+id/aboutFragment"
android:name="com.abc.workshop.AboutFragment"
android:label="About Fragment"
tools:layout="@layout/fragment_about" />
5. ปุ่ม Assign Start Destination ไว้กำหนดหน้าแรกให้กับ Navigation Graph สังเกตได้จากไอคอน Home หรือกำหนดที่ xml ได้เหมือนกัน
app:startDestination="@id/{idของFragment}"
6. เส้น Action ใน editor สามารถลากจาก destination หนึ่งไปยังอีก destination ได้เลย หรือกำหนดใน xml
ผลลัพธ์
<?xml version="1.0" encoding="utf-8"?>
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph_main"
app:startDestination="@id/mainFragment"> <fragment
android:id="@+id/mainFragment"
android:name="com.truedigital.workshop.MainFragment"
android:label="Main Fragment"
tools:layout="@layout/fragment_main"> <action
android:id="@+id/action_mainFragment_to_aboutFragment"
app:destination="@id/aboutFragment" />
</fragment> <fragment
android:id="@+id/aboutFragment"
android:name="com.truedigital.workshop.AboutFragment"
android:label="About Fragment"
tools:layout="@layout/fragment_about" />
</navigation>
แล้วจะเปิด AboutFragment ได้ยังไงล่ะ
- ใส่ <action …/> ใน navigation graph
<action
android:id="@+id/action_mainFragment_to_aboutFragment"
app:destination="@id/aboutFragment" />
ในที่นี้ใส่ภายใน <fragment ../> ของ MainFragment เพราะมี action click ที่ MainFragment และต้องระบุ app:destination (screen ที่ต้องการเปิด) คือ AboutFragment และกำหนด id ให้กับ action
หรือ ใส่ action ใน editor โดยลากเส้นจาก destination ต้นทางไปยังปลายทางค่ะ
2. implement NavController ดังนี้
-- MainFragment.kt --aboutButton.setOnClickListener {
findNavController().navigate(
R.id.action_mainFragment_to_aboutFragment
)
}
เสร็จแล้วววว ไม่ยากเลย
ถ้า MainFragment ต้องการ navigate ไปหน้าอื่น เช่น SettingFragment ทำได้โดยเพิ่ม <action …/> ที่ navigation graph และใช้ NavController ในการ navigate ได้เลยค่ะ
<navigation
...> <fragment
android:id="@+id/mainFragment"
android:name="com.truedigital.workshop.MainFragment"
android:label="Main Fragment"
tools:layout="@layout/fragment_main"> <action
android:id="@+id/action_mainFragment_to_aboutFragment"
app:destination="@id/aboutFragment" />
<action
android:id="@+id/action_mainFragment_to_settingFragment"
app:destination="@id/settingFragment" /> </fragment><fragment
...
.AboutFragment"
... />
<fragment
android:id="@+id/settingFragment"
android:name="com.truedigital.workshop.SettingFragment"
android:label="Setting Fragment"
tools:layout="@layout/fragment_setting" /></navigation>
มี Animation ด้วยนะ
ใส่ Animation ยังไงล่ะ
ใน <action ../> ตามด้านล่างได้เลยค่ะ สามารถ custom animation ได้เองอีกด้วย
<action
android:id="@+id/action_mainFragment_to_aboutFragment"
app:destination="@id/aboutFragment"
app:enterAnim="@anim/slide_in_anim"
app:exitAnim="@anim/slide_out_anim"
app:popEnterAnim="@anim/pop_in_anim"
app:popExitAnim="@anim/pop_out_anim" />
- enterAnim จังหวะเปิด destination ตัวใหม่
- exitAnim จังหวะปิด destination ปัจจุบัน
- popEnterAnim จังหวะเปิด destination ตัวก่อนหน้า เมื่อกด back
- popExitAnim จังหวะ pop destination ปัจจุบันออก เมื่อกด back
ผลลัพธ์
จบแล้วกับการสร้าง Navigation Graph แบบเบื้องต้น ใช้งานง่าย แถมจัดการ fragment transaction ให้อีกด้วย และยังใส่ animation ได้อีก น่าสนใจขนาดนี้
จะไม่ใช้ก็คงไม่ได้แล้วววววว
ฝากติดตาม Part2 เร็วๆนี้ด้วยนะคะ^^