مدير جزء

FragmentManager هو الفئة المسؤولة عن تنفيذ الإجراءات على أجزاء التطبيق، مثل مثل إضافتها أو إزالتها أو استبدالها وإضافتها إلى الحزمة الخلفية.

قد لا تتفاعل مع "FragmentManager" مباشرةً أبدًا إذا كنت تستخدم مكتبة Jetpack Navigation، نظرًا لأنها تعمل مع FragmentManager نيابةً عنك. ومع ذلك، فإن أي تطبيق يستخدم الأجزاء استخدام FragmentManager على مستوى ما، لذلك من المهم فهم ما عليها وكيف تعمل.

تتناول هذه الصفحة ما يلي:

  • كيفية الوصول إلى FragmentManager
  • دور FragmentManager في ما يتعلق بأنشطتك وأجزائك.
  • كيفية إدارة الحزمة الخلفية باستخدام "FragmentManager"
  • كيفية توفير البيانات والتبعيات للأجزاء.

الوصول إلى FragmentManager

يمكنك الوصول إلى FragmentManager من نشاط أو من جزء.

FragmentActivity وفئاتها الفرعية، مثل AppCompatActivity, يمكنك الوصول إلى FragmentManager من خلال getSupportFragmentManager() .

يمكن أن تستضيف الأجزاء جزءًا فرعيًا واحدًا أو أكثر. داخل المنشأة جزء، يمكنك الحصول على مرجع إلى FragmentManager الذي يدير أطفال الجزء من خلال getChildFragmentManager() إذا كنت بحاجة إلى الوصول إلى مضيفه FragmentManager، يمكنك استخدام getParentFragmentManager()

فيما يلي بعض الأمثلة لمعرفة العلاقات بين الأجزاء ومضيفاتها والمثيلات المرتبطة بـ FragmentManager مع كل منها.

مثالان على تخطيط واجهة المستخدم يوضحان العلاقات بين
            الأجزاء وأنشطتها المضيفة
الشكل 1. مثالان على تخطيط واجهة مستخدم يعرضان العلاقات بين الأجزاء والأنشطة المضيفة لها

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

يستضيف جزء المضيف في المثال 1 جزأين فرعيين إنشاء شاشة عرض مقسمة. يستضيف جزء المضيف في المثال 2 جزء فرعي واحد يتألف من جزء العرض عرض التمرير السريع.

وفي ضوء هذا الإعداد، يمكنك اعتبار أن كل مضيف لديه FragmentManager المرتبطة بها التي تدير أجزائها الفرعية. يمكنك توضيح ذلك في الشكل 2 مع تعيينات الخصائص بين supportFragmentManager، parentFragmentManager وchildFragmentManager

يرتبط كل مضيف بـ FragmentManager الخاص به
            الذي يدير أجزائه الفرعية
الشكل 2. لكل مضيف مساعده الخاص FragmentManager المرتبط بالحساب الذي يديره وأجزائها الفرعية

تعتمد سمة FragmentManager المناسبة التي يجب الإشارة إليها على المكان الذي يكون موقع الاتصال في التسلسل الهرمي للأجزاء إلى جانب مدير الأجزاء الذي تحاول الوصول إليه.

بعد إضافة إشارة إلى FragmentManager، يمكنك استخدامها معالجة الأجزاء التي يتم عرضها للمستخدم.

الأجزاء الثانوية

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

إذا أردت إظهار أجزاء متعدّدة في آنٍ واحد، كما هو الحال في العرض المقسّم أو لوحة التحكم، يمكنك استخدام الأجزاء الفرعية التي تديرها جزء الوجهة وإدارة الأجزاء الفرعية التابعة له.

في ما يلي حالات الاستخدام الأخرى للأجزاء الثانوية:

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

استخدام FragmentManager

ويدير FragmentManager تسلسل استدعاء الدوال البرمجية للجزء. في وقت التشغيل، يمكن لـ FragmentManager تنفيذ عمليات حزمة الخلفية مثل الإضافة أو الإزالة الأجزاء استجابةً لتفاعلات المستخدم. تكون كل مجموعة من التغييرات الالتزام معًا كوحدة واحدة تسمى FragmentTransaction للحصول على مناقشة أكثر تفصيلاً حول معاملات التجزئة، راجع دليل المعاملات المجزأة.

عندما ينقر المستخدم على زر الرجوع في جهازه، أو عند الاتصال FragmentManager.popBackStack()، تنبثق معاملة الجزء العلوي من الحزمة. إذا لم يكن هناك أجزاء أخرى المعاملات على المكدس، وإذا كنت لا تستخدم الأجزاء الفرعية، الحدث حتى النشاط. في حال استخدام الأجزاء الثانوية، يُرجى مراجعة اعتبارات خاصة للأجزاء التابعة للأطفال والأجزاء التابعة.

عند الاتصال addToBackStack() إجراء معاملة، يمكن أن تتضمن المعاملة أي عدد من العمليات، مثل إضافة أجزاء متعددة أو استبدال الأجزاء في الأخرى.

عندما يتم تمييز المكدس الخلفي، يتم العمليات العكسية كإجراء بسيط واحد. ومع ذلك، إذا التزمت المعاملات الإضافية قبل إجراء مكالمة popBackStack()، وإذا لم تستخدم addToBackStack() للمعاملة، فإن هذه العمليات لا تتراجع. لذلك، مع تضمين علامة FragmentTransaction واحدة، تجنَّب المعاملات المتداخلة التي تؤثر على الحزمة الخلفية مع تلك التي لا تؤثر عليها.

إجراء معاملة

لعرض جزء ضمن حاوية تصميم، استخدِم السمة FragmentManager. لإنشاء FragmentTransaction. ضمن المعاملة، يمكنك بعد ذلك إجراء add() أو replace() العملية في الحاوية.

على سبيل المثال، قد يظهر FragmentTransaction البسيط على النحو التالي:

Kotlin

supportFragmentManager.commit {
   replace<ExampleFragment>(R.id.fragment_container)
   setReorderingAllowed(true)
   addToBackStack("name") // Name can be null
}

Java

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .replace(R.id.fragment_container, ExampleFragment.class, null)
    .setReorderingAllowed(true)
    .addToBackStack("name") // Name can be null
    .commit();

في هذا المثال، يستبدل ExampleFragment الجزء، إن وجد، حاليًا في حاوية التخطيط المحددة بواسطة معرّف "R.id.fragment_container" يمكن توفير فئة الجزء replace() تتيح الطريقة FragmentManager معالجة إنشاء مثيل باستخدام FragmentFactory لمزيد من المعلومات، يُرجى الاطّلاع على المقالة توفير اعتماديات للأجزاء. .

setReorderingAllowed(true) تحسين تغييرات حالة الأجزاء المتضمنة في المعاملة بحيث تعمل الرسوم المتحركة والانتقالات بشكل صحيح. لمزيد من المعلومات حول والتنقل باستخدام الرسوم المتحركة والتحولات، راجع معاملات التجزئة التنقل بين الأجزاء باستخدام الصور المتحركة.

إجراء المكالمات addToBackStack() ينفّذ المعاملة في الحزمة الخلفية. ويمكن للمستخدم لاحقًا عكس للمعاملة وإعادة الجزء السابق من خلال النقر على زر الرجوع . إذا أضفت أجزاءً متعددة أو أزلتها داخل جزء واحد يتم التراجع عن جميع هذه العمليات عندما يتم تنفيذ العمليات فرقعة. الاسم الاختياري المُقدم في المكالمة addToBackStack() يمنح القدرة على العودة إلى معاملة معينة باستخدام popBackStack()

في حال عدم الاتصال بـ "addToBackStack()" عند إجراء معاملة عند إزالة جزء، ثم يتم إتلاف الجزء الذي تمت إزالته عند إتمام العملية، ولا يمكن للمستخدم الرجوع إليها. إذا كنت عند إزالة جزء، يتم استدعاء addToBackStack()، STOPPED فقط ويصبح لاحقًا RESUMED عند رجوع المستخدم إلى الصفحة السابقة. طريقة العرض يتم إتلافه في هذه الحالة. لمزيد من المعلومات، يُرجى مراجعة دورة حياة التجزئة:

البحث عن جزء حالي

يمكنك الحصول على مرجع إلى الجزء الحالي داخل حاوية تصميم. باستخدام findFragmentById() يمكنك استخدام findFragmentById() للبحث عن جزء إما من خلال رقم التعريف المحدّد عند تضخم من XML أو من خلال معرّف الحاوية عند إضافتها في FragmentTransaction وفي ما يلي مثال لذلك:

Kotlin

supportFragmentManager.commit {
   replace<ExampleFragment>(R.id.fragment_container)
   setReorderingAllowed(true)
   addToBackStack(null)
}
...
val fragment: ExampleFragment =
        supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment

Java

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .replace(R.id.fragment_container, ExampleFragment.class, null)
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .commit();
...
ExampleFragment fragment =
        (ExampleFragment) fragmentManager.findFragmentById(R.id.fragment_container);

بدلاً من ذلك، يمكنك تعيين علامة فريدة للجزء والحصول على الرجوع باستخدام findFragmentByTag() يمكنك تحديد علامة باستخدام سمة XML android:tag على الأجزاء التي ضمن التنسيق أو أثناء السمة add() أو replace() داخل FragmentTransaction.

Kotlin

supportFragmentManager.commit {
   replace<ExampleFragment>(R.id.fragment_container, "tag")
   setReorderingAllowed(true)
   addToBackStack(null)
}
...
val fragment: ExampleFragment =
        supportFragmentManager.findFragmentByTag("tag") as ExampleFragment

Java

FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
    .replace(R.id.fragment_container, ExampleFragment.class, null, "tag")
    .setReorderingAllowed(true)
    .addToBackStack(null)
    .commit();
...
ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");

اعتبارات خاصة للأجزاء الثانوية والأجزاء التابعة

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

لتحديد جزء التنقل الأساسي داخل معاملة مجزأة، طلب setPrimaryNavigationFragment() في العملية، وتمريره في مثيل الجزء الذي childFragmentManager لديه التحكم الأساسي.

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

مسلسل When the Back وقوع الحدث، تتحكم الطبقة الأعمق في سلوك التنقل. بمجرد في الطبقة الأعمق، لا تحتوي على معاملات مجزأة يمكن العودة منها، ويعود التحكم إلى الطبقة التالية، وتتكرر هذه العملية حتى الوصول إلى النشاط.

فعند عرض جزأين أو أكثر في نفس الوقت، أحدهما هو جزء التنقل الأساسي. تعيين جزء لأن جزء التنقل الأساسي يزيل التصنيف من الجزء السابق . باستخدام المثال السابق، إذا قمت بتعيين جزء التفاصيل جزء التنقل الأساسي، تتم إزالة تعيين الجزء الرئيسي.

دعم عدة حزم خلفية

في بعض الحالات، قد يحتاج تطبيقك إلى دعم عدة حِزم خلفية. من الشائع على سبيل المثال، إذا كان تطبيقك يستخدم شريط تنقل سفليًا. FragmentManager السماح يمكنك دعم عدة حزم خلفية باستخدام saveBackStack() restoreBackStack() طريقة تتيح لك هذه الطرق التبديل بين الروابط عن طريق حفظ مكدّس خلفي واحد واستعادة مكدّس آخر.

تعمل ميزة saveBackStack() بشكل مشابه للاتصال بـ popBackStack() باستخدام الخيار الاختياري. مَعلمة name: المعاملة المحدّدة وجميع المعاملات التي تليها في المكدس. ويكمن الفرق في أن saveBackStack() يحفظ لجميع الأجزاء في القسم المعاملات.

على سبيل المثال، لنفترض أنك أضفت في وقت سابق جزءًا إلى المكدس الخلفي عن طريق الالتزام بـ FragmentTransaction باستخدام addToBackStack()، كما هو موضّح في القسم المثال التالي:

Kotlin

supportFragmentManager.commit {
  replace<ExampleFragment>(R.id.fragment_container)
  setReorderingAllowed(true)
  addToBackStack("replacement")
}

Java

supportFragmentManager.beginTransaction()
  .replace(R.id.fragment_container, ExampleFragment.class, null)
  // setReorderingAllowed(true) and the optional string argument for
  // addToBackStack() are both required if you want to use saveBackStack()
  .setReorderingAllowed(true)
  .addToBackStack("replacement")
  .commit();

في هذه الحالة، يمكنك حفظ هذه العملية التي تم تقسيمها وحالة ExampleFragment عن طريق الاتصال بـ saveBackStack():

Kotlin

supportFragmentManager.saveBackStack("replacement")

Java

supportFragmentManager.saveBackStack("replacement");

يمكنك استدعاء restoreBackStack() بنفس معلمة الاسم لاستعادة جميع المعاملات المنبثقة وجميع الأجزاء المحفوظة تنص على ما يلي:

Kotlin

supportFragmentManager.restoreBackStack("replacement")

Java

supportFragmentManager.restoreBackStack("replacement");

توفير التبعيات للأجزاء

عند إضافة جزء، يمكنك إنشاء مثيل للجزء يدويًا وإضافته إلى FragmentTransaction.

Kotlin

fragmentManager.commit {
    // Instantiate a new instance before adding
    val myFragment = ExampleFragment()
    add(R.id.fragment_view_container, myFragment)
    setReorderingAllowed(true)
}

Java

// Instantiate a new instance before adding
ExampleFragment myFragment = new ExampleFragment();
fragmentManager.beginTransaction()
    .add(R.id.fragment_view_container, myFragment)
    .setReorderingAllowed(true)
    .commit();

عند إتمام معاملة التجزئة، يكون مثيل الجزء الذي قمت بإنشائه هو المثيل المستخدم. ومع ذلك، خلال تغيير التهيئة، أو إتلاف النشاط وجميع أجزائه ثم إعادة إنشائها مع الأكثر قابلية للتطبيق موارد Android تعالج السمة FragmentManager كل هذه الإجراءات نيابةً عنك، فهي تعيد إنشاء المثيلات من الأجزاء، ثم يتم إرفاقها بالمضيف، ثم إعادة إنشاء الحزمة الخلفية الولاية.

بشكل تلقائي، تستخدم السمة FragmentManager FragmentFactory الذي يوفره إطار العمل لإنشاء مثيل جديد من الجزء. هذا النمط يستخدم المصنع التلقائي الانعكاس للعثور على دالة إنشائية بدون وسيطة واستدعاءها للجزء الخاص بك. وهذا يعني أنه لا يمكنك استخدام هذا المصنع الافتراضي من التبعيات إلى الجزء الخاص بك. وهذا يعني أيضًا أن أي واجهة مخصصة الدالة الإنشائية التي استخدمتها لإنشاء الجزء في المرة الأولى لا يتم استخدامها أثناء إعادة الإنشاء تلقائيًا.

لتوفير الموارد التابعة للجزء الخاص بك، أو لاستخدام أي رموز الدالة الإنشائية، فبدلاً من ذلك، أنشئ فئة فرعية مخصّصة من نوع FragmentFactory ثم إلغاء FragmentFactory.instantiate يمكنك بعد ذلك إلغاء الإعدادات الأصلية التلقائية لجهاز FragmentManager باستخدام المصنع المخصص، الذي يتم استخدامه لإنشاء مثيل للأجزاء.

لنفترض أن لديك DessertsFragment مسؤولة عن عرض الحلويات الرائجة في بلدتك DessertsFragment تعتمد على فئة DessertsRepository توفر لها المعلومات التي يحتاجها لعرض واجهة المستخدم الصحيحة للمستخدم.

يمكنك تحديد DessertsFragment لطلب DessertsRepository. في الدالة الإنشائية.

Kotlin

class DessertsFragment(val dessertsRepository: DessertsRepository) : Fragment() {
    ...
}

Java

public class DessertsFragment extends Fragment {
    private DessertsRepository dessertsRepository;

    public DessertsFragment(DessertsRepository dessertsRepository) {
        super();
        this.dessertsRepository = dessertsRepository;
    }

    // Getter omitted.

    ...
}

قد يبدو تنفيذ بسيط لـ FragmentFactory مشابهًا لما يلي: ما يلي.

Kotlin

class MyFragmentFactory(val repository: DessertsRepository) : FragmentFactory() {
    override fun instantiate(classLoader: ClassLoader, className: String): Fragment =
            when (loadFragmentClass(classLoader, className)) {
                DessertsFragment::class.java -> DessertsFragment(repository)
                else -> super.instantiate(classLoader, className)
            }
}

Java

public class MyFragmentFactory extends FragmentFactory {
    private DessertsRepository repository;

    public MyFragmentFactory(DessertsRepository repository) {
        super();
        this.repository = repository;
    }

    @NonNull
    @Override
    public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) {
        Class<? extends Fragment> fragmentClass = loadFragmentClass(classLoader, className);
        if (fragmentClass == DessertsFragment.class) {
            return new DessertsFragment(repository);
        } else {
            return super.instantiate(classLoader, className);
        }
    }
}

ينطبق هذا المثال على الفئات الفرعية FragmentFactory، ما يلغي instantiate(). لتوفير منطق إنشاء جزء مخصّص لـ DessertsFragment. ويتم التعامل مع فئات الأجزاء الأخرى من خلال السلوك الافتراضي من FragmentFactory إلى super.instantiate().

يمكنك بعد ذلك ضبط "MyFragmentFactory" كمصنِّع لاستخدامه في حال. إنشاء أجزاء التطبيق عن طريق ضبط خاصية على FragmentManager يجب إعداد هذه السمة قبل إعدادات نشاطك super.onCreate() لضمان استخدام MyFragmentFactory عند إعادة إنشاء أجزائك.

Kotlin

class MealActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        supportFragmentManager.fragmentFactory = MyFragmentFactory(DessertsRepository.getInstance())
        super.onCreate(savedInstanceState)
    }
}

Java

public class MealActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        DessertsRepository repository = DessertsRepository.getInstance();
        getSupportFragmentManager().setFragmentFactory(new MyFragmentFactory(repository));
        super.onCreate(savedInstanceState);
    }
}

يؤدي ضبط FragmentFactory في النشاط إلى إلغاء الجزء. الإنشاء عبر التسلسل الهرمي للأجزاء للنشاط. أو بعبارةٍ أخرى، إنّ childFragmentManager لأي أجزاء فرعية تضيفها يستخدم السمة المخصصة. تم ضبط إعدادات المصنع في هذا الحقل ما لم يتم إلغاؤه على مستوى أقل.

الاختبار باستخدام Fragmentfactor

في بنية نشاط واحد، اختبر أجزائك في بالعزل باستخدام FragmentScenario الصف. بما أنّه لا يمكنك الاعتماد على منطق onCreate المخصّص نشاط، يمكنك بدلاً من ذلك تمرير FragmentFactory كوسيطة في اختبار الأجزاء، كما هو موضح في المثال التالي:

// Inside your test
val dessertRepository = mock(DessertsRepository::class.java)
launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment {
    // Test Fragment logic
}

للحصول على معلومات تفصيلية حول عملية الاختبار هذه وأمثلة كاملة، راجِع اختبار الأجزاء.