העברת נתונים בין יעדים

בעזרת הניווט אפשר לצרף נתונים לפעולות ניווט על ידי הגדרת ארגומנטים ליעד. לדוגמה, יעד של פרופיל משתמש עשוי לקבל ארגומנטים של מזהי משתמשים כדי לקבוע איזה משתמש יוצג.

באופן כללי, מומלץ מאוד להעביר רק את כמות הנתונים המינימלית בין היעדים. לדוגמה, צריך להעביר מפתח כדי לאחזר אובייקט, במקום להעביר את האובייקט עצמו, כי נפח האחסון הכולל של כל המצבים השמורים מוגבל ב-Android. אם אתם צריכים להעביר כמויות גדולות של נתונים, השתמשו ב-ViewModel כפי שמתואר בסקירה הכללית על ViewModel.

הגדרת ארגומנטים של יעד

כדי להעביר נתונים בין יעדים, קודם צריך להגדיר את הארגומנט על ידי הוספה שלו ליעד שמקבל אותו, לפי השלבים הבאים:

  1. בNavigation Editor, לוחצים על היעד שמקבל את הארגומנט.
  2. בחלונית מאפיינים, לוחצים על הוספה (+).
  3. בחלון Add Argument Link שמופיע, מזינים את שם הארגומנט, את סוג הארגומנט, את האפשרות אם הארגומנט יכול להיות null ואת ערך ברירת המחדל, אם יש צורך.
  4. לוחצים על הוספה. שימו לב שהארגומנט מופיע עכשיו ברשימה Arguments בחלונית Attributes.
  5. לאחר מכן, לוחצים על הפעולה המתאימה שתיקח אתכם ליעד הזה. בחלונית Attributes, הארגומנט החדש יתווסף לקטע Argument Default Values.
  6. אפשר גם לראות שהארגומנט נוסף ב-XML. לוחצים על הכרטיסייה Text כדי לעבור לתצוגת XML, ומבחינים שהארגומנט נוסף ליעד שמקבל את הארגומנט. דוגמה מופיעה בהמשך:

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

סוגי הארגומנטים הנתמכים

בספריית הניווט יש תמיכה בסוגי הארגומנטים הבאים:

סוג תחביר של app:argType תמיכה בערכי ברירת מחדל טיפול על ידי נתיבים Nullable
מספר שלם app:argType="integer" כן כן לא
בלונים app:argType="float" כן כן לא
ארוך app:argType="long" כן – ערכי ברירת המחדל תמיד צריכים להסתיים בסיומת 'L' (למשל '123L'). כן לא
בוליאני app:argType="boolean" כן – 'true' או 'false' כן לא
מחרוזת 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".

אפשר לנתח מחרוזות כדי לקבל מסלולים, קישורי עומק ומזהי URI עם הארגומנטים שלהם. אי אפשר לעשות זאת באמצעות סוגי נתונים מותאמים אישית כמו Parcelables ו-Serializables, כפי שמוצג בטבלה הקודמת. כדי להעביר נתונים מורכבים בהתאמה אישית, שומרים את הנתונים במקום אחר, כמו ViewModel או מסד נתונים, ומעבירים רק מזהה במהלך הניווט. לאחר סיום הניווט, מאחזרים את הנתונים במיקום החדש.

כשבוחרים באחד מהסוגים בהתאמה אישית, מופיעה תיבת הדו-שיח Select Class (בחירת כיתה) עם בקשה לבחור את הכיתה המתאימה לסוג הזה. בכרטיסייה Project אפשר לבחור כיתה מהפרויקט הנוכחי.

אפשר לבחור באפשרות <inferred type> כדי לאפשר לספריית הניווט לקבוע את הסוג על סמך הערך שסופק.

אפשר לסמן את האפשרות Array כדי לציין שהארגומנט צריך להיות מערך של הערך שנבחר ב-Type. חשוב לזכור:

  • אין תמיכה במערכים של ערכים מוגדרים מראש ובמערכים של הפניות למשאבים.
  • מערכי נתונים תומכים בערכים nullable, ללא קשר לתמיכה בערכים nullable של הסוג הבסיסי. לדוגמה, השימוש ב-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, לא תוכלו להשתמש בפלאגין SafeArgs. במקרים כאלה, אפשר להשתמש ב-Bundles כדי להעביר נתונים ישירות.

כדי להוסיף 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")
    }
}

בנוסף, צריך להחיל אחד משני הפלאגינים הזמינים.

כדי ליצור קוד בשפת 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. משתמשים ב-method 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);
}

בקוד של היעד המקבל, משתמשים ב-method‏ getArguments() כדי לאחזר את החבילה ולהשתמש בתוכן שלה. כשמשתמשים ביחסי התלות של -ktx, משתמשי Kotlin יכולים להשתמש גם ב-delegate של המאפיין 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);

בקוד של היעד המקבל, משתמשים ב-method‏ 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 ליעד ההתחלה:

כדי לאחזר את הנתונים ביעד ההתחלה, צריך להפעיל את הפונקציה Fragment.getArguments().

שיקולים לגבי ProGuard

אם אתם מקצים קוד, עליכם למנוע ערפול של שמות המחלקות Parcelable,‏ Serializable ו-Enum כחלק מתהליך ההקטנה. יש שתי דרכים לעשות את זה:

  • להשתמש בהערות ב-Keep.
  • שימוש בכללי שמירה של שמות.

בקטעים הבאים מפורטות הגישות האלה.

שימוש בהערות ב-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

סרטונים