FragmentManager
کلاسی است که مسئول انجام اقداماتی بر روی قطعات برنامه شما است، مانند افزودن، حذف یا جایگزینی آنها و افزودن آنها به پشته پشتی.
اگر از کتابخانه Jetpack Navigation استفاده می کنید، ممکن است هرگز مستقیماً با FragmentManager
تعامل نداشته باشید، زیرا از طرف شما با FragmentManager
کار می کند. با این حال، هر برنامهای که از قطعات استفاده میکند، در سطوحی FragmentManager
استفاده میکند، بنابراین مهم است که بدانیم چیست و چگونه کار میکند.
این صفحه شامل:
- نحوه دسترسی به
FragmentManager
- نقش
FragmentManager
در رابطه با فعالیت ها و قطعات شما. - نحوه مدیریت پشته پشتی با
FragmentManager
. - نحوه ارائه داده ها و وابستگی ها به قطعات خود.
به FragmentManager دسترسی پیدا کنید
شما می توانید از یک اکتیویتی یا از یک قطعه به FragmentManager
دسترسی داشته باشید.
FragmentActivity
و زیر کلاس های آن، مانند AppCompatActivity
، از طریق متد getSupportFragmentManager()
به FragmentManager
دسترسی دارند.
قطعات می توانند میزبان یک یا چند قطعه کودک باشند. در داخل یک قطعه، می توانید یک مرجع به FragmentManager
که فرزندان قطعه را از طریق getChildFragmentManager()
مدیریت می کند، دریافت کنید. اگر نیاز به دسترسی به میزبان FragmentManager
دارید، می توانید از getParentFragmentManager()
استفاده کنید.
در اینجا چند مثال برای مشاهده روابط بین قطعات، میزبان آنها و نمونه های FragmentManager
مرتبط با هر کدام آورده شده است.
شکل 1 دو مثال را نشان می دهد که هر کدام یک میزبان فعالیت واحد دارند. فعالیت میزبان در هر دوی این مثالها، ناوبری سطح بالا را به عنوان BottomNavigationView
به کاربر نشان میدهد که مسئول تعویض قطعه میزبان با صفحههای مختلف در برنامه است. هر صفحه به عنوان یک قطعه جداگانه پیاده سازی می شود.
قطعه میزبان در مثال 1 میزبان دو قطعه فرزند است که یک صفحه نمایش تقسیم شده را تشکیل می دهند. قطعه میزبان در مثال 2 میزبان یک قطعه فرزند منفرد است که قطعه نمایش یک نمای تند کشیدن را تشکیل می دهد.
با توجه به این تنظیمات، می توانید در مورد هر میزبانی فکر کنید که یک FragmentManager
مرتبط با آن است که قطعات فرزند خود را مدیریت می کند. این در شکل 2 همراه با نگاشت ویژگی بین supportFragmentManager
، parentFragmentManager
و childFragmentManager
نشان داده شده است.
ویژگی 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 }
برای اطلاعات دقیق در مورد این فرآیند تست و برای مثالهای کامل، به تست قطعات خود مراجعه کنید.