Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> ระบบ >> Android

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize

Design Application มัก Cumbersome บน Whiteboard ลูกศรชี้จากจุดนี้ไปจุดนั่น App ที่คิดว่ามี 1-2 Activities กลายเป็น Multiple Flows, Fragments, User Interactions เยอะ ไม่น่าจะดีถ้าเอาบน Whiteboard มา Replicate ใน Code ได้ง่าย?

Say Hello to Navigation Component 👋

สำหรับมือใหม่ Navigation Component ไม่ใช่ UI Class 替代 Layout ของ Activity/Fragment คิดว่าเป็น Map: แทน Continents = Fragments ต้องมี Directions ไประหว่าง Continents แสดง Fragments และ Connections แบบ Top-down บทความนี้ครอบ Main Aspects และ Integration

Ready to Set Sail? ⛵️

Learning The Ropes

Navigation Component ใช้ได้ตั้งแต่ Android Studio 3.3+ เพิ่ม Dependencies:

android { ... } 
dependencies { 
    implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0' 
    implementation 'androidx.navigation:navigation-ui-ktx:2.0.0' 
}

สมมติ Design App โครงสร้าง:

  • Start Fragment
  • Fragment A
  • Fragment B

User ไป Fragment A หรือ B จาก Start Fragment

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize
Application ของเรา

โดยไม่มี Navigation Component: ใช้ Code เปิด Fragment ตอน Click Button ( 익숙한 Code)

val myFragment: MyFragment = MyFragment() 
supportFragmentManager.beginTransaction().add(R.id.container, myFragment).commit()

ตัวอย่างเล็กนี้ Code สั้น Uncomplicated แต่ไม่ Scale หาก App ใหญ่ Complex User Flows

All Aboard

เริ่ม Navigation Component: สร้าง Navigation Graph Graph = Map Outline User Flow สร้าง: Right-click res Folder → New Resource File ชื่อ user_flow_graph.xml Type = Navigation

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize
สร้าง Resource File ใหม่

ทุกการเดินทางมี Home Base เราเรียกว่า NavHost Placeholder สำหรับ Destinations Swap เมื่อ User Interact UI Add NavHost ใน Activity Main Layout:

<?xml version="1.0" encoding="utf-8"?> 
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android" 
    xmlns:tools="https://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    xmlns:app="https://schemas.android.com/apk/res-auto" 
    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:layout_constraintLeft_toLeftOf="parent" 
        app:layout_constraintRight_toRightOf="parent" 
        app:layout_constraintTop_toTopOf="parent" 
        app:layout_constraintBottom_toBottomOf="parent" 
        app:defaultNavHost="true" 
        app:navGraph="@navigation/user_flow_graph" /> 
</androidx.constraintlayout.widget.ConstraintLayout>

เพิ่ม Fragment Element ที่จะ Display/Swap Fragments สังเกต Attribute navGraph Link ไป XML ที่สร้าง

ต้อง Add Starting Destination ไม่งั้น Compile Error

เปิด user_flow_graph.xml คลิก Plus Icon 小さいใน Navigation Editor:

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize
Add A Destination

Menu Pop Up: สร้าง Placeholder (Fill Later) หรือเลือกจาก Fragments ที่มี:

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize
Destination Choices

User Flow เริ่มจาก Start Fragment เลือกมันก่อน

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize
Home Base (สังเกต Icon 🏠)

เพิ่ม Fragment A และ B

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize
Destinations ทั้งหมด

Connect Destinations: Click Dot ที่โผล่ Hover Destination Drag ไปอีกตัว

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize
Neat! 😎

Connection ระหว่าง Start Fragment และ A, B เรียกว่า Actions

Shiver me Timbers

คิดว่า Connect Destinations แล้วเสร็จ Magic จะทำงาน? ไม่ได้

ต้องบอก Code Navigate ไป Destination ยังไง? มี Magic นิดหน่อย

1. Add Gradle Plugin Safe Args Ensure Type Safety Navigate ระหว่าง Destinations

buildscript { /... } 
dependencies { 
    ... 
    classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.0.0" 
} }

เพิ่ม Plugin ใน App build.gradle:

apply plugin: "androidx.navigation.safeargs.kotlin" 

ตรวจสอบ android.useAndroidX=true ใน gradle.properties

ทำไมต้อง Config ทั้งนี้? ตอนสร้าง Actions Android Studio Generate Code ใช้ Activate Actions Code = Methods/Classes Represent Actions ใช้ Start Fragment Example Generated Code Actions: Class StartFragmentDirections Methods = Actions ที่สร้าง:

  • StartFragmentDirections.actionStartFragmentToFragmentA()
  • StartFragmentDirections.actionStartFragmentToFragmentB()

Actions → Code แล้ว ใช้งาน:

val action = StartFragmentDirections.actionStartFragmentToFragmentA() 

Last Step: ใช้ NavController Manage Navigation ใน NavHost Access ด้วย 3 วิธี:

  • Fragment.findNavController()
  • View.findNavController()
  • Activity.findNavController(viewId: Int)

รวมกัน:

fragmentABtn.setOnClickListener { button -> 
    val action = StartFragmentDirections.actionStartFragmentToFragmentA() 
    button.findNavController().navigate(action) 
}

Trim Your Sails

อยาก Pass Data ระหว่าง Destinations? Scenario: User Click Item อยากทำอะไรกับ Item ใน Next Destination ใช้ Destination Arguments เปิด user_flow_graph.xml คลิก Fragment A ด้านขวา Attributes Menu มี Arguments

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize

Add Argument: Click ➕ Popup Configure Argument: Name, Type, Default Value เพิ่ม Argument String Type ใน Fragment A = Message จาก Start Fragment

เชี่ยวชาญ Android Navigation Component: สร้าง Implement และ Optimize

ใน Start Fragment ที่ Define Action เรียก Generated Method ส่ง Argument:

fragmentABtn.setOnClickListener { button -> 
    val action = StartFragmentDirections.actionStartFragmentToFragmentA("Hello From Start Fragment") 
    button.findNavController().navigate(action) 
}

Access ใน Fragment A 2 วิธี:

  1. Access Bundle Get Message Value
class FragmentA: Fragment() { 
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 
        val bundle = arguments 
        val root = inflater.inflate(R.layout.fragment_a, container, false) 
        val textView: TextView = root.findViewById(R.id.textView) 
        textView.text = bundle?.getString("message") 
        return root 
    } 
}
  1. ใช้ navArgs (ถ้าใช้ -ktx Dependencies)
class FragmentA: Fragment() { 
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 
        val args: FragmentAArgs by navArgs() 
        val root = inflater.inflate(R.layout.fragment_a, container, false) 
        val textView: TextView = root.findViewById(R.id.textView) 
        textView.text = args.message 
        return root 
    } 
}

✋ ใช้ navArgs ต้อง Enable Java8 ใน build.gradle

โค้ดทั้งหมดอยู่ใน GitHub Repository นี้