مدیر بخش‌بندی

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

اگر از کتابخانه Jetpack Navigation استفاده می‌کنید، ممکن است هرگز مستقیماً با FragmentManager تعامل نداشته باشید، زیرا این کتابخانه ازطرف شما با FragmentManager کار می‌کند. بااین‌حال، هر برنامه‌ای که از قطعه‌ها استفاده می‌کند، در سطحی از FragmentManager استفاده می‌کند، بنابراین مهم است که بدانید چیست و چگونه کار می‌کند.

این صفحه شامل موارد زیر است:

  • نحوه دسترسی به FragmentManager.
  • نقش FragmentManager در ارتباط با فعالیت‌ها و قطعه‌های شما.
  • نحوه مدیریت پشته قبلی‌ها با FragmentManager.
  • نحوه ارائه داده‌ها و وابستگی‌ها به قطعه‌های شما.

دسترسی به FragmentManager

می‌توانید ازطریق فعالیت یا قطعه به FragmentManager دسترسی پیدا کنید.

FragmentActivity و زیرکلاس‌های آن، مثل AppCompatActivity، ازطریق روش getSupportFragmentManager() به FragmentManager دسترسی دارند.

تکه‌ها می‌توانند میزبان یک یا چند تکه فرزند باشند. درون یک قطعه، می‌توانید ازطریق getChildFragmentManager() به FragmentManager که فرزندان قطعه را مدیریت می‌کند ارجاع دهید. اگر لازم است به میزبان FragmentManager دسترسی داشته باشید، می‌توانید از getParentFragmentManager() استفاده کنید.

در اینجا چند نمونه برای دیدن روابط بین قطعه‌ها، میزبان‌های آن‌ها، و نمونه‌های FragmentManager مرتبط با هرکدام آورده شده است.

دو نمونه چیدمان میانای کاربر که روابط بین
            تکه‌ها و فعالیت‌های میزبان آن‌ها را نشان می‌دهد
شکل ۱. دو نمونه چیدمان میانای کاربر که رابطه بین قطعه‌ها و فعالیت‌های میزبان آن‌ها را نشان می‌دهد.

شکل ۱ دو نمونه را نشان می‌دهد که هرکدام یک میزبان فعالیت دارند. فعالیت میزبان در هر دوی این مثال‌ها پیمایش سطح بالا را به‌صورت BottomNavigationView به کاربر نشان می‌دهد که مسئول تعویض قطعه میزبان با صفحه‌های مختلف در برنامه است. هر صفحه به‌عنوان قطعه‌ای جداگانه پیاده‌سازی می‌شود.

تکه میزبان در «مثال ۱» میزبان دو تکه فرزند است که صفحه نمای دوقسمتی را تشکیل می‌دهند. تکه میزبان در مثال ۲ میزبان یک تکه فرزند است که تکه نمایش نمای تند کشیدن را تشکیل می‌دهد.

با توجه به این راه‌اندازی، می‌توانید هر میزبان را به‌عنوان یک FragmentManager مرتبط با آن درنظر بگیرید که قطعات فرزند خود را مدیریت می‌کند. این موضوع در شکل ۲ همراه با نگاشت‌های دارایی بین supportFragmentManager، parentFragmentManager، و childFragmentManager نشان داده شده است.

هر میزبان FragmentManager منسوب به خود را دارد
            که قطعه‌های فرزند آن را مدیریت می‌کند
شکل ۲. هر میزبان FragmentManager منسوب به خود را دارد که تکه‌های فرزند آن را مدیریت می‌کند.

ویژگی FragmentManager مناسب برای ارجاع به این بستگی دارد که محل فراخوانی در کجای سلسله‌مراتب تکه قرار دارد و می‌خواهید به کدام مدیر تکه دسترسی پیدا کنید.

وقتی مرجعی برای FragmentManager داشته باشید، می‌توانید از آن برای دستکاری قطعه‌هایی که به کاربر نمایش داده می‌شود استفاده کنید.

بخش‌های فرزند

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

اگر می‌خواهید چند قطعه را به‌طور هم‌زمان نشان دهید، مثلاً در نمای دونیمه یا داشبورد، می‌توانید از قطعه‌های فرزند استفاده کنید که توسط قطعه مقصد و مدیر قطعه فرزند آن مدیریت می‌شوند.

سایر موارد استفاده از قطعه‌های فرزند عبارت‌اند از:

  • اسلایدهای صفحه، بااستفاده از ViewPager2 در قطعه والد برای مدیریت مجموعه‌ای از نماهای قطعه فرزند.
  • پیمایش فرعی در مجموعه‌ای از صفحه‌های مرتبط.
  • Jetpack Navigation از قطعه‌های فرزند به‌عنوان مقصدهای مجزا استفاده می‌کند. یک فعالیت میزبان یک والد NavHostFragment است و فضای آن را با قطعات مقصد فرزند مختلف پر می‌کند، درحالی‌که کاربران در برنامه شما پیمایش می‌کنند.

استفاده از FragmentManager

‫FragmentManager پشته قبلی‌های قطعه را مدیریت می‌کند. در زمان اجرا، FragmentManager می‌تواند عملیات پشته قبلی‌ها را مانند افزودن یا برداشتن تکه‌ها در پاسخ به تعاملات کاربر انجام دهد. هر مجموعه تغییرات باهم به‌عنوان یک واحد واحد به‌نام FragmentTransaction ثبت می‌شود. برای بحث عمیق‌تر درباره تراکنش‌های قطعه، به راهنمای تراکنش‌های قطعه مراجعه کنید.

وقتی کاربر روی دکمه «برگشت» در دستگاهش تک‌ضرب می‌زند، یا وقتی FragmentManager.popBackStack() را فرامی‌خوانید، بالاترین تراکنش قطعه از پشته خارج می‌شود. اگر تراکنش قطعه دیگری در پشته وجود نداشته باشد و از قطعه‌های فرزند استفاده نمی‌کنید، رویداد «برگشت» به فعالیت منتقل می‌شود. اگر از قطعه‌های فرزند استفاده می‌کنید، ملاحظات ویژه برای قطعه‌های فرزند و همزاد را ببینید.

وقتی addToBackStack() را در تراکنشی فراخوانی می‌کنید، تراکنش می‌تواند شامل هر تعداد عملیات باشد، مثلاً افزودن چند قطعه یا جایگزین کردن قطعه‌ها در چند محفظه.

وقتی پشته قبلی‌ها باز می‌شود، همه این عملیات‌ها به‌صورت یک کنش اتمی واحد معکوس می‌شوند. بااین‌حال، اگر تراکنش‌های بیشتری را قبل‌از فراخوانی popBackStack() انجام داده باشید و اگر از addToBackStack() برای تراکنش استفاده نکرده باشید، این عملیات‌ها برگردانده نمی‌شوند. بنابراین، در یک FragmentTransaction، از درهم‌بافی تراکنش‌هایی که بر پشته قبلی‌ها تأثیر می‌گذارند با تراکنش‌هایی که تأثیر نمی‌گذارند اجتناب کنید.

انجام تراکنش

برای نمایش قطعه در محفظه چیدمان، از FragmentManager برای ایجاد FragmentTransaction استفاده کنید. سپس در تراکنش می‌توانید عملیات add() یا replace() را روی محفظه انجام دهید.

برای مثال، یک FragmentTransaction ساده ممکن است این‌گونه باشد:

کاتلین

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

جاوا

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) تغییرات حالت قطعه‌های درگیر در تراکنش را بهینه‌سازی می‌کند تا پویانمایی‌ها و گذارها به‌درستی کار کنند. برای اطلاعات بیشتر درباره پیمایش با پویانمایی و انتقال، به تراکنش‌های Fragment و پیمایش بین Fragment بااستفاده از پویانمایی مراجعه کنید.

با فراخوانی addToBackStack() تراکنش به پشته قبلی‌ها منتقل می‌شود. کاربر می‌تواند بعداً با تک‌ضرب زدن روی دکمه «برگشتن»، تراکنش را معکوس کند و قطعه قبلی را برگرداند. اگر چند قطعه را در یک تراکنش اضافه یا حذف کرده باشید، وقتی پشته قبلی‌ها باز می‌شود، همه آن عملیات‌ها واگرد می‌شود. نام اختیاری ارائه‌شده در فراخوانی addToBackStack() به شما امکان می‌دهد بااستفاده از popBackStack() به تراکنش خاصی برگردید.

اگر هنگام انجام تراکنشی که قطعه‌ای را برمی‌دارد addToBackStack() را فراخوانی نکنید، قطعه برداشته‌شده هنگام انجام تراکنش ازبین می‌رود و کاربر نمی‌تواند به آن برگردد. اگر هنگام برداشتن تکه، addToBackStack() را فراخوانی کنید، تکه فقط STOPPED می‌شود و بعداً وقتی کاربر به عقب پیمایش می‌کند، RESUMED می‌شود. در این حالت، نمای آن ازبین می‌رود. برای اطلاعات بیشتر، به چرخه حیات Fragment مراجعه کنید.

پیدا کردن قطعه موجود

بااستفاده از findFragmentById() می‌توانید به قطعه فعلی در محفظه چیدمان ارجاع دهید. از findFragmentById() برای جستجوی قطعه با شناسه ارائه‌شده هنگام گسترش از XML یا با شناسه محفظه هنگام افزودن در FragmentTransaction استفاده کنید. مثالی در اینجا ارائه شده است:

کاتلین

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

جاوا

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 تعریف شده‌اند اختصاص دهید.

کاتلین

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

جاوا

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 آن کنترل اصلی دارد ارسال کنید.

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

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

وقتی دو یا چند قطعه به‌طور هم‌زمان نمایش داده می‌شود، فقط یکی از آن‌ها قطعه ناوبری اصلی است. تنظیم یک قطعه به‌عنوان قطعه ناوبری اصلی، این عنوان را از قطعه قبلی حذف می‌کند. بااستفاده از مثال قبلی، اگر قطعه جزئیات را به‌عنوان قطعه پیمایش اصلی تنظیم کنید، تعیین قطعه اصلی برداشته می‌شود.

پشتیبانی از چند پشته قبلی‌ها

در برخی موارد، ممکن است برنامه‌تان نیاز به پشتیبانی از چند پشته قبلی‌ها داشته باشد. یک نمونه رایج زمانی است که برنامه‌تان از نوار پیمایش پایین استفاده می‌کند. FragmentManager به شما امکان می‌دهد بااستفاده از روش‌های saveBackStack() و restoreBackStack() از چندین پشته قبلی‌ها پشتیبانی کنید. این روش‌ها به شما امکان می‌دهند با ذخیره کردن یک پشته قبلی‌ها و بازیابی پشته قبلی‌های دیگر، بین پشته قبلی‌ها جابه‌جا شوید.

saveBackStack() عملکردی مشابه فراخوانی popBackStack() با پارامتر اختیاری name دارد: تراکنش مشخص‌شده و همه تراکنش‌های بعداز آن در پشته پاپ می‌شوند. تفاوت این است که saveBackStack() وضعیت همه قطعه‌ها را در تراکنش‌های خارج‌شده ذخیره می‌کند.

برای مثال، فرض کنید قبلاً با ثبت FragmentTransaction بااستفاده از addToBackStack()، قطعه‌ای را به پشته قبلی‌ها اضافه کرده‌اید، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

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

جاوا

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() ذخیره کنید:

کاتلین

supportFragmentManager.saveBackStack("replacement")

جاوا

supportFragmentManager.saveBackStack("replacement");

می‌توانید restoreBackStack() را با همان پارامتر نام فراخوانی کنید تا همه تراکنش‌های خارج‌شده و همه حالت‌های قطعه ذخیره‌شده را بازیابی کنید:

کاتلین

supportFragmentManager.restoreBackStack("replacement")

جاوا

supportFragmentManager.restoreBackStack("replacement");

ارائه وابستگی‌ها به قطعه‌های شما

هنگام افزودن قطعه، می‌توانید قطعه را به‌صورت دستی نمونه‌سازی کنید و آن را به FragmentTransaction اضافه کنید.

کاتلین

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

جاوا

// 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 در سازنده خود نیاز داشته باشد.

کاتلین

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

جاوا

public class DessertsFragment extends Fragment {
    private DessertsRepository dessertsRepository;

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

    // Getter omitted.

    ...
}

پیاده‌سازی ساده FragmentFactory شما ممکن است شبیه به مورد زیر باشد.

کاتلین

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

جاوا

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() مدیریت می‌شوند.

سپس می‌توانید با تنظیم ویژگی در FragmentManager، MyFragmentFactory را به‌عنوان کارخانه برای استفاده هنگام ساختن قطعه‌های برنامه‌تان تعیین کنید. باید این ویژگی را قبل‌از super.onCreate() فعالیتتان تنظیم کنید تا مطمئن شوید که هنگام بازسازی قطعاتتان از MyFragmentFactory استفاده می‌شود.

کاتلین

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

جاوا

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 هر تکه فرعی که اضافه می‌کنید از مجموعه کارخانه تکه سفارشی در اینجا استفاده می‌کند، مگراینکه در سطح پایین‌تری لغو شود.

آزمایش با FragmentFactory

در معماری فعالیت واحد، قطعه‌هایتان را به‌طور مجزا بااستفاده از کلاس FragmentScenario آزمایش کنید. ازآنجایی‌که نمی‌توانید به منطق سفارشی onCreate فعالیتتان اتکا کنید، می‌توانید به‌جای آن FragmentFactory را به‌عنوان آرگومان به آزمایش قطعه‌هایتان ارسال کنید، همان‌طور که در مثال زیر نشان داده شده است:

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

برای کسب اطلاعات دقیق درباره این فرایند آزمایش و مشاهده نمونه‌های کامل، به آزمایش کردن قطعه‌ها مراجعه کنید.