ส่งข้อมูลระหว่างจุดหมาย

การนำทางช่วยให้คุณแนบข้อมูลไปยังการดำเนินการนำทางได้โดยกำหนดอาร์กิวเมนต์สำหรับปลายทาง เช่น ปลายทางโปรไฟล์ผู้ใช้อาจใช้อาร์กิวเมนต์รหัสผู้ใช้เพื่อระบุผู้ใช้ที่จะแสดง

โดยทั่วไป คุณควรส่งข้อมูลเพียงจํานวนน้อยที่สุดระหว่างปลายทาง เช่น คุณควรส่งคีย์เพื่อเรียกข้อมูลออบเจ็กต์ แทนการส่งออบเจ็กต์นั้นๆ เนื่องจากพื้นที่เก็บข้อมูลทั้งหมดสำหรับสถานะที่บันทึกไว้ทั้งหมดมีขีดจำกัดใน Android หากต้องการส่งข้อมูลจํานวนมาก ให้ใช้ ViewModel ตามที่อธิบายไว้ในภาพรวมของ ViewModel

กำหนดอาร์กิวเมนต์ปลายทาง

หากต้องการส่งข้อมูลระหว่างปลายทาง ให้กําหนดอาร์กิวเมนต์ก่อนโดยเพิ่มลงในปลายทางที่รับข้อมูล โดยทําตามขั้นตอนต่อไปนี้

  1. ในเครื่องมือแก้ไขการนําทาง ให้คลิกปลายทางที่รับอาร์กิวเมนต์
  2. ในแผงแอตทริบิวต์ ให้คลิกเพิ่ม (+)
  3. ในหน้าต่างเพิ่มลิงก์อาร์กิวเมนต์ที่ปรากฏขึ้น ให้ป้อนชื่ออาร์กิวเมนต์ ประเภทอาร์กิวเมนต์ ระบุว่าอาร์กิวเมนต์เป็นค่าที่อนุญาตหรือไม่ และค่าเริ่มต้น หากจำเป็น
  4. คลิกเพิ่ม โปรดทราบว่าตอนนี้อาร์กิวเมนต์จะปรากฏในรายการอาร์กิวเมนต์ในแผงแอตทริบิวต์
  5. จากนั้นคลิกการดําเนินการที่เกี่ยวข้องซึ่งนําคุณไปยังปลายทางนี้ ในแผงแอตทริบิวต์ คุณควรเห็นอาร์กิวเมนต์ที่เพิ่มใหม่ในส่วนค่าเริ่มต้นของอาร์กิวเมนต์
  6. นอกจากนี้ คุณยังเห็นว่ามีการเพิ่มอาร์กิวเมนต์ใน XML ด้วย คลิกแท็บข้อความเพื่อสลับไปยังมุมมอง XML และสังเกตว่าระบบได้เพิ่มอาร์กิวเมนต์ของคุณไปยังปลายทางที่รับอาร์กิวเมนต์ ตัวอย่างแสดงอยู่ด้านล่าง

     <fragment android:id="@+id/myFragment" >
         <argument
             android:name="myArg"
             app:argType="integer"
             android:defaultValue="0" />
     </fragment>
    

ประเภทอาร์กิวเมนต์ที่รองรับ

ไลบรารีการนําทางรองรับประเภทอาร์กิวเมนต์ต่อไปนี้

ประเภท ไวยากรณ์ app:argType การรองรับค่าเริ่มต้น จัดการโดยเส้นทาง เว้นว่างได้
จำนวนเต็ม app:argType="integer" ใช่ ได้ ไม่
ล่องลอย app:argType="float" ใช่ ได้ ไม่
ยาว app:argType="long" ใช่ - ค่าเริ่มต้นต้องลงท้ายด้วยส่วนต่อท้าย "L" เสมอ (เช่น "123L") ใช่ ไม่
บูลีน app:argType="boolean" ใช่ - "จริง" หรือ "เท็จ" ใช่ ไม่
สตริง app:argType="string" ใช่ ได้ ใช่
การอ้างอิงทรัพยากร app:argType="reference" ได้ - ค่าเริ่มต้นต้องอยู่ในรูปแบบ "@resourceType/resourceName" (เช่น "@style/myCustomStyle") หรือ "0" ใช่ ไม่
Parcelable ที่กําหนดเอง app:argType="<type>" โดยที่ <type> คือชื่อคลาสที่สมบูรณ์ในตัวเองของ Parcelable รองรับค่าเริ่มต้น "@null" ไม่รองรับค่าเริ่มต้นอื่นๆ ไม่ ใช่
Serializable ที่กําหนดเอง app:argType="<type>" โดยที่ <type> คือชื่อคลาสที่สมบูรณ์ในตัวเองของ Serializable รองรับค่าเริ่มต้น "@null" ไม่รองรับค่าเริ่มต้นอื่นๆ ไม่ ใช่
Enum ที่กําหนดเอง app:argType="<type>" โดยที่ <type> คือชื่อที่สมบูรณ์ในตัวเองของ enum ใช่ - ค่าเริ่มต้นต้องตรงกับชื่อที่ไม่มีข้อจำกัดความ (เช่น "SUCCESS" เพื่อจับคู่กับ MyEnum.SUCCESS) ไม่ ไม่

หากประเภทอาร์กิวเมนต์รองรับค่า Null คุณสามารถประกาศค่าเริ่มต้นเป็น Null ได้โดยใช้ android:defaultValue="@null"

ระบบจะแยกวิเคราะห์เส้นทาง, Deep Link และ URI พร้อมอาร์กิวเมนต์จากสตริงได้ ซึ่งไม่สามารถทำได้โดยใช้ประเภทข้อมูลที่กําหนดเอง เช่น Parcelables และ Serializable ดังที่เห็นในตารางก่อนหน้า หากต้องการส่งผ่านข้อมูลที่ซับซ้อนที่กําหนดเอง ให้จัดเก็บข้อมูลไว้ที่อื่น เช่น ViewModel หรือฐานข้อมูล และส่งผ่านตัวระบุขณะไปยังส่วนต่างๆ เท่านั้น จากนั้นดึงข้อมูลในตําแหน่งใหม่หลังจากไปยังส่วนต่างๆ เสร็จแล้ว

เมื่อเลือกประเภทที่กำหนดเองรายการใดรายการหนึ่ง กล่องโต้ตอบเลือกชั้นเรียนจะปรากฏขึ้นและแจ้งให้คุณเลือกชั้นเรียนที่เกี่ยวข้องสำหรับประเภทนั้น แท็บโปรเจ็กต์จะให้คุณเลือกชั้นเรียนจากโปรเจ็กต์ปัจจุบัน

คุณเลือก <inferred type> เพื่อให้คลังการนำทางระบุประเภทตามค่าที่ระบุได้

คุณสามารถเลือกอาร์เรย์เพื่อระบุว่าอาร์กิวเมนต์ควรเป็นอาร์เรย์ของค่าประเภทที่เลือก ข้อควรทราบ

  • ระบบไม่รองรับอาร์เรย์ของ Enum และอาร์เรย์ของการอ้างอิงทรัพยากร
  • อาร์เรย์รองรับค่าที่อนุญาตค่า Null โดยไม่คำนึงถึงการสนับสนุนค่าที่อนุญาตค่า Null ของประเภทพื้นฐาน ตัวอย่างเช่น การใช้ app:argType="integer[]" จะช่วยให้คุณใช้ app:nullable="true" เพื่อระบุว่าระบบยอมรับการพาสอาร์เรย์ Null ได้
  • อาร์เรย์รองรับค่าเริ่มต้นค่าเดียว "@null" อาร์เรย์ไม่รองรับค่าเริ่มต้นอื่นๆ

ลบล้างอาร์กิวเมนต์ปลายทางในการดําเนินการ

การดำเนินการทั้งหมดที่ไปยังปลายทางจะใช้อาร์กิวเมนต์ระดับปลายทางและค่าเริ่มต้น หากจําเป็น คุณสามารถลบล้างค่าเริ่มต้นของอาร์กิวเมนต์ (หรือตั้งค่าหากยังไม่มี) ได้โดยกําหนดอาร์กิวเมนต์ที่ระดับการดําเนินการ โดยอาร์กิวเมนต์นี้ต้องมีชื่อและประเภทเดียวกับอาร์กิวเมนต์ที่ประกาศไว้ในปลายทาง

XML ต่อไปนี้ประกาศการดำเนินการที่มีอาร์กิวเมนต์ซึ่งลบล้างอาร์กิวเมนต์ระดับปลายทางจากตัวอย่างก่อนหน้า

<action android:id="@+id/startMyFragment"
    app:destination="@+id/myFragment">
    <argument
        android:name="myArg"
        app:argType="integer"
        android:defaultValue="1" />
</action>

ใช้ Safe Args เพื่อส่งข้อมูลโดยที่ปลอดภัยจากประเภท

คอมโพเนนต์การนำทางมีพลักอิน Gradle ชื่อ Safe Args ที่สร้างคลาสออบเจ็กต์และคลาสบิลเดอร์แบบง่ายสำหรับการนําทางที่ปลอดภัยตามประเภทและการเข้าถึงอาร์กิวเมนต์ที่เกี่ยวข้อง เราขอแนะนำอย่างยิ่งให้ใช้ Safe Args ในการไปยังส่วนต่างๆ และส่งผ่านข้อมูล เนื่องจากช่วยให้มั่นใจได้ว่าประเภทข้อมูลจะปลอดภัย

หากไม่ได้ใช้ Gradle คุณจะใช้ปลั๊กอิน Safe Args ไม่ได้ ในกรณีเหล่านี้ คุณสามารถใช้ Bundle เพื่อส่งผ่านข้อมูลได้โดยตรง

หากต้องการเพิ่ม Safe Args ลงในโปรเจ็กต์ ให้ใส่ classpath ต่อไปนี้ในไฟล์ build.gradle ระดับบนสุด

Groovy

buildscript {
    repositories {
        google()
    }
    dependencies {
        def nav_version = "2.8.4"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Kotlin

buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.8.4"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}

นอกจากนี้ คุณยังต้องใช้ปลั๊กอิน 1 ใน 2 รายการที่มีให้

หากต้องการสร้างโค้ดภาษา Java ที่เหมาะสมกับโมดูล Java หรือโมดูล Java และ Kotlin แบบผสม ให้เพิ่มบรรทัดนี้ลงในไฟล์ build.gradle ของแอปหรือโมดูล

Groovy

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs")
}

หรือหากต้องการสร้างโค้ด Kotlin ที่เหมาะสมสําหรับโมดูล Kotlin เท่านั้น ให้เพิ่ม

Groovy

plugins {
  id 'androidx.navigation.safeargs.kotlin'
}

Kotlin

plugins {
    id("androidx.navigation.safeargs.kotlin")
}

คุณต้องมี android.useAndroidX=true ในไฟล์ gradle.properties ตามหัวข้อการย้ายข้อมูลไปยัง AndroidX

หลังจากเปิดใช้ Safe Args แล้ว โค้ดที่สร้างขึ้นจะมีคลาสและเมธอดที่ปลอดภัยประเภทต่อไปนี้สําหรับการดำเนินการแต่ละรายการ รวมถึงปลายทางการส่งและปลายทางการรับแต่ละรายการ

  • ระบบจะสร้างคลาสสําหรับปลายทางแต่ละแห่งที่เป็นแหล่งที่มาของการดำเนินการ ชื่อของคลาสนี้คือชื่อของปลายทางต้นทางต่อท้ายด้วยคำว่า "เส้นทาง" เช่น หากปลายทางต้นทางคือข้อมูลโค้ดที่ชื่อ SpecifyAmountFragment คลาสที่สร้างขึ้นจะมีชื่อว่า SpecifyAmountFragmentDirections

    คลาสนี้มีเมธอดสําหรับการดําเนินการแต่ละรายการที่กําหนดไว้ในต้นทางและปลายทาง

  • สําหรับการดําเนินการแต่ละรายการที่ใช้ส่งอาร์กิวเมนต์ ระบบจะสร้างคลาสภายในที่มีชื่อตามการดําเนินการนั้น เช่น หากการดําเนินการเรียกว่า confirmationAction, คลาสจะชื่อ ConfirmationAction หากการดําเนินการของคุณมีพารามิเตอร์ที่ไม่มี defaultValue คุณจะใช้คลาสการดําเนินการที่เกี่ยวข้องเพื่อกําหนดค่าของพารามิเตอร์

  • ระบบจะสร้างชั้นเรียนสําหรับปลายทางที่รับ ชื่อของคลาสนี้คือชื่อปลายทางต่อท้ายด้วยคำว่า "Args" เช่น หากข้อมูลโค้ดปลายทางชื่อ ConfirmationFragment, คลาสที่สร้างขึ้นจะชื่อ ConfirmationFragmentArgs ใช้fromBundle() เมธอดของคลาสนี้เพื่อดึงข้อมูลอาร์กิวเมนต์

ตัวอย่างต่อไปนี้แสดงวิธีใช้เมธอดเหล่านี้เพื่อตั้งค่าอาร์กิวเมนต์และส่งไปยังเมธอด navigate()

Kotlin

override fun onClick(v: View) {
   val amountTv: EditText = view!!.findViewById(R.id.editTextAmount)
   val amount = amountTv.text.toString().toInt()
   val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
   v.findNavController().navigate(action)
}

Java

@Override
public void onClick(View view) {
   EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount);
   int amount = Integer.parseInt(amountTv.getText().toString());
   ConfirmationAction action =
           SpecifyAmountFragmentDirections.confirmationAction();
   action.setAmount(amount);
   Navigation.findNavController(view).navigate(action);
}

ในโค้ดสําหรับปลายทางที่รับ ให้ใช้เมธอด getArguments() เพื่อดึงข้อมูลกลุ่มและใช้เนื้อหา เมื่อใช้ -ktx ผู้ใช้ Kotlin จะใช้ตัวรับมอบสิทธิ์พร็อพเพอร์ตี้ by navArgs() เพื่อเข้าถึงอาร์กิวเมนต์ได้ด้วย

Kotlin

val args: ConfirmationFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val tv: TextView = view.findViewById(R.id.textViewAmount)
    val amount = args.amount
    tv.text = amount.toString()
}

Java

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    TextView tv = view.findViewById(R.id.textViewAmount);
    int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
    tv.setText(amount + "");
}

ใช้ Safe Args กับการดำเนินการส่วนกลาง

เมื่อใช้ Safe Args กับการดำเนินการส่วนกลาง คุณจะต้องระบุค่า android:id สำหรับองค์ประกอบ <navigation> รูท ดังที่แสดงในตัวอย่างต่อไปนี้

<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/main_nav"
            app:startDestination="@id/mainFragment">

    ...

</navigation>

การนําทางจะสร้างคลาส Directions สําหรับองค์ประกอบ <navigation> โดยอิงตามค่า android:id เช่น หากคุณมีองค์ประกอบ <navigation> ที่มี android:id=@+id/main_nav คลาสที่สร้างขึ้นจะเรียกว่า MainNavDirections ปลายทางทั้งหมดภายในองค์ประกอบ <navigation> จะมีเมธอดที่สร้างขึ้นสําหรับการเข้าถึงการดําเนินการส่วนกลางที่เกี่ยวข้องทั้งหมดโดยใช้เมธอดเดียวกันตามที่อธิบายไว้ในส่วนก่อนหน้า

ส่งข้อมูลระหว่างปลายทางด้วยออบเจ็กต์ Bundle

หากไม่ได้ใช้ Gradle คุณจะยังคงส่งอาร์กิวเมนต์ระหว่างปลายทางได้โดยใช้ออบเจ็กต์ Bundle สร้างออบเจ็กต์ Bundle และส่งไปยังปลายทางโดยใช้ navigate() ดังตัวอย่างต่อไปนี้

Kotlin

val bundle = bundleOf("amount" to amount)
view.findNavController().navigate(R.id.confirmationAction, bundle)

Java

Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);

ในโค้ดของปลายทางที่รับ ให้ใช้เมธอด getArguments() เพื่อดึงข้อมูล Bundle และใช้เนื้อหา

Kotlin

val tv = view.findViewById<TextView>(R.id.textViewAmount)
tv.text = arguments?.getString("amount")

Java

TextView tv = view.findViewById(R.id.textViewAmount);
tv.setText(getArguments().getString("amount"));

ส่งข้อมูลไปยังปลายทางเริ่มต้น

คุณสามารถส่งข้อมูลไปยังปลายทางเริ่มต้นของแอป ก่อนอื่น คุณต้องสร้าง Bundle ที่เก็บข้อมูลอย่างชัดเจน ถัดไป ให้ใช้แนวทางใดแนวทางหนึ่งต่อไปนี้เพื่อส่ง Bundle ไปยังปลายทางเริ่มต้น

  • หากสร้าง NavHost โดยใช้โปรแกรม ให้เรียกใช้ NavHostFragment.create(R.navigation.graph, args) โดยที่ args คือ Bundle ที่เก็บข้อมูลของคุณ
  • หรือจะตั้งค่าอาร์กิวเมนต์ปลายทางเริ่มต้นโดยเรียกใช้ NavController.setGraph() แบบโอเวอร์โหลดรายการใดรายการหนึ่งต่อไปนี้ก็ได้

หากต้องการเรียกข้อมูลในปลายทางเริ่มต้น ให้เรียกใช้ Fragment.getArguments()

ข้อควรพิจารณาเกี่ยวกับ ProGuard

หากลดขนาดโค้ด คุณต้องป้องกันไม่ให้ชื่อคลาส Parcelable, Serializable และ Enum ได้รับการสร้างความสับสนในกระบวนการลดขนาด คุณสามารถทำได้ด้วยวิธีใดวิธีหนึ่งใน 2 วิธีต่อไปนี้

  • ใช้คำอธิบายประกอบ @Keep
  • ใช้กฎ keepnames

ส่วนย่อยต่อไปนี้จะอธิบายแนวทางเหล่านี้

ใช้คำอธิบายประกอบ @Keep

ตัวอย่างต่อไปนี้จะเพิ่มคำอธิบายประกอบ @Keep ลงในคำจำกัดความของคลาสโมเดล

Kotlin

@Keep class ParcelableArg : Parcelable { ... }

@Keep class SerializableArg : Serializable { ... }

@Keep enum class EnumArg { ... }

Java

@Keep public class ParcelableArg implements Parcelable { ... }

@Keep public class SerializableArg implements Serializable { ... }

@Keep public enum EnumArg { ... }

ใช้กฎ keepnames

นอกจากนี้ คุณยังเพิ่มกฎ keepnames ลงในไฟล์ proguard-rules.pro ได้ด้วย ดังที่แสดงในตัวอย่างต่อไปนี้

proguard-rules.pro

...

-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg

...

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับการนําทางได้จากแหล่งข้อมูลเพิ่มเติมต่อไปนี้

Codelabs

วิดีโอ