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

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

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

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

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

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

يمكنك اختيار <inferred 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>

استخدام Safe Args لتمرير البيانات مع أمان النوع

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

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

لإضافة Safe Args إلى مشروعك، أدرِج classpath التالي في ملف build.gradle من المستوى الأعلى:

رائع

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 في تطبيقك أو وحدتك:

رائع

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، يحتوي الرمز الذي تم إنشاؤه على الأنواع التالية من الفئات والأساليب الآمنة لكل إجراء، بالإضافة إلى كل وجهة إرسال وتلقّي.

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

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

إذا لم تكن تستخدم 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() ل retrieving the 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

...

مصادر إضافية

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

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

الفيديوهات