انتقال داده بین مقصدها

«ناوبری» به شما امکان می‌دهد با تعریف کردن آرگومان‌هایی برای مقصد، داده‌ها را به عملیات ناوبری پیوست کنید. برای مثال، مقصد نمایه کاربر ممکن است آرگومان شناسه کاربر را بگیرد تا تعیین کند کدام کاربر نمایش داده شود.

به‌طورکلی، باید به‌شدت ترجیح دهید که فقط حداقل مقدار داده را بین مقصدها منتقل کنید. برای مثال، باید کلیدی را برای بازیابی شیء ارسال کنید نه اینکه خود شیء را ارسال کنید، زیرا کل فضای همه حالت‌های ذخیره‌شده در 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» پشتیبانی می‌کند. از مقادیر پیش‌فرض دیگر پشتیبانی نمی‌کند. نه بله
Custom Serializable app:argType="<type>"، که در آن <type> نام کلاس کاملاً واجدشرایط Serializable است از مقدار پیش‌فرض «@null» پشتیبانی می‌کند. از مقادیر پیش‌فرض دیگر پشتیبانی نمی‌کند. نه بله
Enum سفارشی app:argType="<type>"، که <type> نام کاملاً واجد شرایط enum است بله - مقادیر پیش‌فرض باید با نام غیرواجدشرایط مطابقت داشته باشد (مثلاً «SUCCESS» برای مطابقت با MyEnum.SUCCESS). نه نه

اگر نوع آرگومان از مقادیر تهی پشتیبانی می‌کند، می‌توانید مقدار پیش‌فرض تهی را بااستفاده از android:defaultValue="@null" اعلام کنید.

مسیرها، پیوندهای عمیق، و URIها با متغیرهای مستقلشان را می‌توان از رشته‌ها تجزیه کرد. این کار بااستفاده از انواع داده‌های سفارشی مانند Parcelables و Serializables که در جدول قبلی دیده می‌شود امکان‌پذیر نیست. برای ارسال داده‌های پیچیده سفارشی، داده‌ها را در جای دیگری مانند ViewModel یا پایگاه داده ذخیره کنید و فقط یک شناسه را هنگام پیمایش ارسال کنید؛ سپس داده‌ها را در مکان جدید پس‌از پایان پیمایش بازیابی کنید.

وقتی یکی از انواع سفارشی را انتخاب می‌کنید، کادر گفتگوی انتخاب کلاس ظاهر می‌شود و از شما می‌خواهد کلاس مربوط به آن نوع را انتخاب کنید. برگه پروژه به شما امکان می‌دهد کلاسی را از پروژه فعلی‌تان انتخاب کنید.

می‌توانید <inferred type> را انتخاب کنید تا کتابخانه Navigation نوع را براساس مقدار ارائه‌شده تعیین کند.

می‌توانید آرایه را علامت بزنید تا نشان دهید که آرگومان باید آرایه‌ای از مقدار نوع انتخاب‌شده باشد. به موارد زیر توجه کنید:

  • آرایه‌های شمارشی و آرایه‌های ارجاع منبع پشتیبانی نمی‌شوند.
  • آرایه‌ها از مقادیر تهی‌پذیر پشتیبانی می‌کنند، صرف‌نظر از اینکه نوع پایه از مقادیر تهی‌پذیر پشتیبانی کند یا نه. برای مثال، استفاده از app:argType="integer[]" به شما امکان می‌دهد از app:nullable="true" استفاده کنید تا نشان دهید که ارسال آرایه تهی قابل‌قبول است.
  • آرایه‌ها از یک مقدار پیش‌فرض واحد، «@null»، پشتیبانی می‌کنند. آرایه‌ها از هیچ مقدار پیش‌فرض دیگری پشتیبانی نمی‌کنند.

لغو کردن آرگومان مقصد در کنش

همه کنش‌هایی که به مقصد پیمایش می‌کنند از آرگومان‌ها و مقادیر پیش‌فرض سطح مقصد استفاده می‌کنند. درصورت نیاز، می‌توانید مقدار پیش‌فرض یک آرگومان را لغو کنید (یا اگر ازقبل وجود ندارد، یکی تنظیم کنید) با تعریف یک آرگومان در سطح کنش. این متغیر مستقل باید نام و نوع یکسانی با متغیر مستقل اعلام‌شده در مقصد داشته باشد.

XML زیر کنشی را با متغیر مستقلی اعلام می‌کند که متغیر مستقل سطح مقصد را از مثال قبلی لغو می‌کند:

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

استفاده از «آرگومان‌های ایمن» برای انتقال داده با ایمنی تایپی

مؤلفه Navigation دارای یک افزونه Gradle به‌نام Safe Args است که کلاس‌های سازنده و شیء ساده‌ای را برای پیمایش ایمن تایپی و دسترسی به هر آرگومان مرتبط تولید می‌کند. استفاده از Safe Args برای پیمایش و انتقال داده‌ها به‌شدت توصیه می‌شود، زیرا ایمنی تایپی را تضمین می‌کند.

اگر از Gradle استفاده نمی‌کنید، نمی‌توانید از افزونه Safe Args استفاده کنید. در این موارد، می‌توانید از Bundles استفاده کنید تا مستقیماً داده‌ها را منتقل کنید.

برای افزودن آرگومان‌های ایمن به پروژه خود، classpath زیر را در فایل build.gradle سطح بالای خود وارد کنید:

گرووی

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

کاتلین

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

همچنین باید یکی از دو افزونه موجود را اعمال کنید.

برای تولید کد زبان جاوا که برای ماژول‌های جاوا یا ترکیبی از جاوا و کاتلین مناسب باشد، این خط را به فایل build.gradle برنامه یا ماژول خود اضافه کنید:

گرووی

plugins {
  id 'androidx.navigation.safeargs'
}

کاتلین

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

روش دیگر، برای تولید کد کاتلین مناسب برای ماژول‌های فقط کاتلین، اضافه کردن:

گرووی

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

کاتلین

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

شما باید طبق دستور Migrating to AndroidX، در فایل gradle.properties خود، android.useAndroidX=true را داشته باشید.

پس‌از فعال کردن «آرگومان‌های ایمن»، کد تولیدشده شما حاوی کلاس‌ها و روش‌های ایمنی تایپی زیر برای هر کنش و همچنین برای هر مقصد ارسال و دریافت است.

  • برای هر مقصدی که کنش از آنجا شروع می‌شود، یک کلاس ایجاد می‌شود. نام این کلاس نام مقصد مبدأ است که کلمه «مسیرها» به آن اضافه شده است. برای مثال، اگر مقصد مبدأ قطعه‌ای به‌نام SpecifyAmountFragment باشد، کلاس تولیدشده SpecifyAmountFragmentDirections نامیده می‌شود.

    این کلاس برای هر کنش تعریف‌شده در مقصد مبدأ، یک روش دارد.

  • برای هر کنشی که برای ارسال آرگومان استفاده می‌شود، یک کلاس داخلی ایجاد می‌شود که نام آن براساس کنش است. برای مثال، اگر کنش confirmationAction, نامیده شود، کلاس ConfirmationAction نامیده می‌شود. اگر کنش شما حاوی آرگومان‌های بدون defaultValue باشد، از کلاس کنش مرتبط برای تنظیم مقدار آرگومان‌ها استفاده می‌کنید.

  • کلاسی برای مقصد دریافت‌کننده ایجاد می‌شود. نام این کلاس نام مقصد است که کلمه «Args» به آن اضافه شده است. برای مثال، اگر نام قطعه مقصد ConfirmationFragment, باشد، نام کلاس تولیدشده ConfirmationFragmentArgs خواهد بود. از روش fromBundle() این کلاس برای بازیابی آرگومان‌ها استفاده کنید.

مثال زیر نشان می‌دهد که چگونه از این روش‌ها برای تنظیم آرگومان و ارسال آن به روش navigate() استفاده کنید:

کاتلین

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)
}

جاوا

@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() نیز برای دسترسی به آرگومان‌ها استفاده کنند.

کاتلین

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()
}

جاوا

@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 + "");
}

استفاده از «آرگومان‌های ایمن» با کنش سراسری

هنگام استفاده از «آرگومان‌های ایمن» با کنش سراسری، باید مقدار 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() به مقصد ارسال کنید، مانند مثال زیر:

کاتلین

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

جاوا

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

در کد مقصد گیرنده، از روش getArguments() برای بازیابی Bundle و استفاده از محتوای آن استفاده کنید:

کاتلین

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

جاوا

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

ارسال داده به مقصد شروع

می‌توانید داده‌ها را به مقصد شروع برنامه‌تان منتقل کنید. ابتدا باید به‌طور صریح Bundle بسازید که داده‌ها را نگه دارد. سپس، از یکی از رویکردهای زیر برای ارسال Bundle به مقصد شروع استفاده کنید:

برای بازیابی داده‌ها در مقصد شروع، با Fragment.getArguments() تماس بگیرید.

ملاحظات ProGuard

اگر درحال کوچک کردن کدتان هستید، باید از مبهم‌سازی نام کلاس‌های Parcelable، Serializable، و Enum به‌عنوان بخشی از فرایند کوچک‌سازی جلوگیری کنید. این کار را می‌توانید به یکی از دو روش زیر انجام دهید:

  • از حاشیه‌نویسی‌های Keep @استفاده کنید.
  • از قوانین keepnames استفاده کنید.

زیربخش‌های زیر این رویکردها را شرح می‌دهند.

استفاده از حاشیه‌نویسی‌های Keep@

مثال زیر حاشیه‌نویسی‌های @Keep را به تعاریف کلاس مدل اضافه می‌کند:

کاتلین

@Keep class ParcelableArg : Parcelable { ... }

@Keep class SerializableArg : Serializable { ... }

@Keep enum class EnumArg { ... }

جاوا

@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

ویدیوها