تمرير البيانات بين الوجهات

يتيح لك التنقل إرفاق بيانات بعملية تنقُّل من خلال تحديد الوسيطات للوجهة. على سبيل المثال، قد تأخذ وجهة الملف الشخصي للمستخدم وسيطة رقم تعريف المستخدم لتحديد المستخدم الذي سيتم عرضه.

بوجه عام، يجب أن تفضل بشدة تمرير الحد الأدنى فقط من البيانات بين الوجهات. على سبيل المثال، يجب تمرير مفتاح لاسترداد كائن بدلاً من تمرير الكائن نفسه، لأن المساحة الإجمالية لجميع الحالات المحفوظة تكون محدودة في 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>
    

أنواع الوسائط المتوافقة

تتوافق مكتبة التنقّل مع أنواع الوسيطات التالية:

Type بنية 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" نعم لا
حزمة مخصّصة app:argType="<type>"، حيث يكون <type> هو اسم الفئة المؤهَّل بالكامل لـ Parcelable يتوافق مع قيمة تلقائية لـ "@null". لا يتوافق مع القيم التلقائية الأخرى. لا نعم
قابل للتسلسل المخصّص app:argType="<type>"، حيث يكون <type> هو اسم الفئة المؤهَّل بالكامل لـ Serializable يتوافق مع قيمة تلقائية لـ "@null". لا يتوافق مع القيم التلقائية الأخرى. لا نعم
قيم تعداد مخصّص app:argType="<type>"، حيث يكون <type> هو الاسم المؤهّل بالكامل للتعداد نعم - يجب أن تتطابق القيم التلقائية مع الاسم غير المؤهَّل (على سبيل المثال، "نجاح" لمطابقة MyEnum.SUCCESS). لا لا

إذا كان نوع الوسيطة يتيح استخدام القيم الفارغة، يمكنك تعريف القيمة التلقائية بالقيمة "فارغ" باستخدام السمة android:defaultValue="@null".

يمكن تحليل المسارات وروابط الصفحات في التطبيق وعناوين URI مع وسيطاتها من السلاسل. لا يمكن إجراء ذلك باستخدام أنواع البيانات المخصّصة مثل Parcelables وSerialables كما هو موضّح في الجدول السابق. لتمرير البيانات المعقدة المخصصة، خزن البيانات في مكان آخر مثل ViewModel أو قاعدة البيانات، ومرر مُعرّفًا فقط أثناء التنقل؛ ثم استرجع البيانات في الموقع الجديد بعد انتهاء التنقل.

عند اختيار أحد الأنواع المخصّصة، يظهر مربع الحوار اختيار صف ويطلب منك اختيار الفئة المناسبة لهذا النوع. تتيح لك علامة تبويب المشروع اختيار فئة من مشروعك الحالي.

يمكنك اختيار <intered type> لجعل مكتبة التنقل تحدد النوع استنادًا إلى القيمة المقدمة.

يمكنك التحقق من صفيف للإشارة إلى أن الوسيطة يجب أن تكون مصفوفة من قيمة النوع المحددة. ملاحظات:

  • لا يتوفر مصفوفات التعداد ومصفوفات مراجع الموارد.
  • تدعم المصفوفات القيم القابلة للقيم الفارغة، بصرف النظر عن دعم القيم القابلة للقيم الفارغة من النوع الأساسي. على سبيل المثال، يتيح لك استخدام 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>

استخدام الوسيطات الآمنة لتمرير البيانات بنوع أمان

يحتوي مكوِّن التنقل على مكوّن Gradle الإضافي يُسمى Safe Args، والذي يُنشئ فئات كائنات ومنشئات بسيطة للتنقل الآمن من النوع والوصول إلى أي وسيطات مرتبطة. يوصى بشدة باستخدام الوسيطات الآمنة للتنقل وتمرير البيانات، لأنها تضمن أمان النوع.

إذا كنت لا تستخدم Gradle، لا يمكنك استخدام المكوّن الإضافي Safe Args. وفي هذه الحالات، يمكنك استخدام الحِزم لتمرير البيانات مباشرةً.

لإضافة الوسيطات الآمنة إلى مشروعك، عليك تضمين classpath التالي في ملف build.gradle ذي المستوى الأعلى:

رائع

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

Kotlin

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

يجب أيضًا تطبيق أحد المكونين الإضافيين المتاحين.

لإنشاء رمز لغة Java مناسب لوحدات Java أو وحدات Kotlin المختلطة، أضِف هذا السطر إلى ملف build.gradle للتطبيق أو الوحدة:

رائع

plugins {
  id 'androidx.navigation.safeargs'
}

Kotlin

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

بدلاً من ذلك، لإنشاء رمز Kotlin المناسب للوحدات التي تستخدم لغة Kotlin فقط، أضِف ما يلي:

رائع

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

Kotlin

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

يجب أن يكون لديك android.useAndroidX=true في ملف gradle.properties وفقًا لنقل البيانات إلى AndroidX.

بعد تفعيل الوسيطات الآمنة (Safe Args)، تحتوي التعليمة البرمجية التي أنشأتها على النوع التالي للفئات والطرق الآمنة لكل إجراء وكذلك مع كل وجهة إرسال واستلام.

  • يتمّ إنشاء فئة لكل وجهة ينشأ فيها إجراء معيّن. اسم هذه الفئة هو اسم الوجهة الأصلية الملحق بكلمة "Directions" (الاتجاهات). على سبيل المثال، إذا كانت الوجهة الأصلية عبارة عن جزء يُسمّى 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 + "");
}

استخدام الوسيطات الآمنة مع إجراء عام

عند استخدام الوسيطات الآمنة مع إجراء عام، يجب توفير قيمة 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> طرقًا للوصول إلى كل الإجراءات العامة المرتبطة باستخدام الطرق نفسها الموضحة في القسم السابق.

تمرير البيانات بين الوجهات باستخدام عناصر الحزمة

إذا لم تكن تستخدم 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 إلى وجهة البداية:

لاسترداد البيانات في وجهة البداية، يمكنك الاتصال بالرقم Fragment.getArguments().

اعتبارات ProGuard

في حال كنت تقلّص الرموز، عليك منع إخفاء مفاتيح فك تشفير أسماء الفئات Parcelable وSerializable وEnum كجزء من عملية التصغير. يمكنك القيام بذلك بإحدى الطريقتين التاليتين:

  • استخدِم تعليقات @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

...

مراجع إضافية

لمعرفة المزيد حول التنقل، راجع الموارد الإضافية التالية.

عيّنات

الدروس التطبيقية حول الترميز

الفيديوهات الطويلة