مدیر بخش

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

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

این صفحه شامل:

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

به FragmentManager دسترسی پیدا کنید

شما می توانید از یک اکتیویتی یا از یک قطعه به FragmentManager دسترسی داشته باشید.

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

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

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

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

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

قطعه میزبان در مثال 1 میزبان دو قطعه فرزند است که یک صفحه نمایش تقسیم شده را تشکیل می دهند. قطعه میزبان در مثال 2 میزبان یک قطعه فرزند منفرد است که قطعه نمایش یک نمای تند کشیدن را تشکیل می دهد.

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

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

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

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

تکه های کودک

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

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

موارد استفاده دیگر برای قطعات کودک به شرح زیر است:

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

از FragmentManager استفاده کنید

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

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

هنگامی که در یک تراکنش 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 خود مدیریت کند. برای اطلاعات بیشتر، بخش Provide dependencies to your fragments را ببینید.

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

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

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

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

با استفاده از 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() یک مرجع دریافت کنید. می‌توانید با استفاده از android:tag XML یک تگ به قطعاتی که در طرح‌بندی شما یا در طول عملیات 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 کنترل اولیه آن را دارد، عبور دهید.

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

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

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

پشته های متعدد پشتیبان را پشتیبانی کنید

در برخی موارد، برنامه شما ممکن است نیاز به پشتیبانی از چندین پشته پشتی داشته باشد. یک مثال معمول این است که برنامه شما از نوار پیمایش پایینی استفاده می کند. 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() مدیریت می‌شوند.

سپس می‌توانید MyFragmentFactory به‌عنوان کارخانه‌ای برای استفاده در هنگام ساخت قطعات برنامه خود با تنظیم یک ویژگی در FragmentManager تعیین کنید. شما باید این ویژگی را قبل از 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
}

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