Execute Fragment Transaction Again on Pop
FragmentManager is the class responsible for performing actions on your app's fragments, such every bit adding, removing, or replacing them, and adding them to the dorsum stack.
You lot might never interact with FragmentManager straight if you're using the Jetpack Navigation library, equally information technology works with the FragmentManager on your behalf. That said, any app using fragments is using FragmentManager at some level, so it'due south important to understand what it is and how information technology works.
This topic covers how to access the FragmentManager, the part of the FragmentManager in relation to your activities and fragments, managing the back stack with FragmentManager, and providing data and dependencies to your fragments.
Access the FragmentManager
Accessing in an activity
Every FragmentActivity and subclasses thereof, such as AppCompatActivity, have admission to the FragmentManager through the getSupportFragmentManager() method.
Accessing in a Fragment
Fragments are also capable of hosting i or more kid fragments. Inside a fragment, you tin get a reference to the FragmentManager that manages the fragment'south children through getChildFragmentManager(). If you need to admission its host FragmentManager, you can utilise getParentFragmentManager().
Permit's wait at a couple of examples to run into the relationships between fragments, their hosts, and the FragmentManager instances associated with each.
Figure 1 shows two examples, each of which has a unmarried activity host. The host activeness in both of these examples displays top-level navigation to the user every bit a BottomNavigationView that is responsible for swapping out the host fragment with unlike screens in the app, with each screen implemented as a separate fragment.
The host fragment in Instance i , hosts two child fragments that make up a split up-view screen. The host fragment in Example 2 , hosts a single child fragment that makes up the display fragment of a swipe view.
Given this setup, you can call up most each host as having a FragmentManager associated with it which manages its kid fragments. This is illustrated in the effigy two, forth with property mappings betwixt supportFragmentManager, parentFragmentManager and childFragmentManager.
FragmentManager associated with it that manages its child fragments.The appropriate FragmentManager property to reference depends on where the callsite is in the fragment hierarchy along with which fragment manager you are trying to access.
Once yous have a reference to the FragmentManager, you can use it to dispense the fragments beingness displayed to the user.
Child fragments
Generally speaking, your app should consist of a single or small number of activities in your application project, with each activity representing a group of related screens. The activity may provide a point to place top-level navigation and a identify to scope ViewModels and other view-country between fragments. Each private destination in your app should be represented by a fragment.
If you want to bear witness multiple fragments at one time, such as in a split-view or a dashboard, you should use child fragments that are managed by your destination fragment and its child fragment manager.
Other apply cases for kid fragments may include the post-obit:
- Screen slides, with a
ViewPager2in a parent fragment to manage a serial of kid fragment views. - Sub-navigation within a set up of related screens.
- Jetpack Navigation uses kid fragments as individual destinations. An activity hosts a single parent
NavHostFragmentand fills its space with unlike kid destination fragments as users navigate through your app.
Using the FragmentManager
The FragmentManager manages the fragment back stack. At runtime, the FragmentManager tin perform back stack operations like adding or removing fragments in response to user interactions. Each set of changes are committed together as a single unit chosen a FragmentTransaction. For a more in-depth discussion about fragment transactions, see the fragment transactions guide.
When the user presses the Back push on their device, or when you call FragmentManager.popBackStack(), the superlative-most fragment transaction is popped off of the stack. In other words, the transaction is reversed. If there are no more fragment transactions on the stack, and if you aren't using child fragments, the back issue bubbles up to the activity. If you are using child fragments, run into special considerations for child and sibling fragments.
When you call addToBackStack() on a transaction, note that the transaction can include any number of operations, such as adding multiple fragments, replacing fragments in multiple containers, and and then on. When the dorsum stack is popped, all of these operations are reversed equally a single atomic action. If you've committed additional transactions prior to the popBackStack() telephone call, and if you did not utilise addToBackStack() for the transaction, these operations are not reversed. Therefore, inside a single FragmentTransaction, avoid interleaving transactions that impact the back stack with those that do not.
Perform a transaction
To display a fragment within a layout container, apply the FragmentManager to create a FragmentTransaction. Within the transaction, you can then perform an add() or replace() operation on the container.
For example, a simple FragmentTransaction might await like this:
Kotlin
supportFragmentManager.commit { supervene upon<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(truthful) addToBackStack("proper name") // name can exist nothing } Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.grade, null) .setReorderingAllowed(true) .addToBackStack("proper name") // name can exist null .commit(); In this example, ExampleFragment replaces the fragment, if whatsoever, that is currently in the layout container identified by the R.id.fragment_container ID. Providing the fragment's form to the supersede() method allows the FragmentManager to handle instantiation using its FragmentFactory. For more than information, see Providing dependencies.
setReorderingAllowed(true) optimizes the state changes of the fragments involved in the transaction so that animations and transitions piece of work correctly. For more than information on navigating with animations and transitions, see Fragment transactions and Navigate between fragments using animations.
Calling addToBackStack() commits the transaction to the back stack. The user can later reverse the transaction and bring back the previous fragment by pressing the Back button. If you added or removed multiple fragments within a single transaction, all of those operations are undone when the back stack is popped. The optional name provided in the addToBackStack() telephone call gives yous the ability to popular back to that specific transaction using popBackStack().
If you don't call addToBackStack() when you perform a transaction that removes a fragment, then the removed fragment is destroyed when the transaction is committed, and the user cannot navigate back to information technology. If you lot do call addToBackStack() when removing a fragment, then the fragment is only STOPPED and is later on RESUMED when the user navigates back. Notation that its view is destroyed in this case. For more information, meet Fragment lifecycle.
Finding an existing fragment
You tin can get a reference to the current fragment within a layout container past using findFragmentById(). Utilise findFragmentById() to wait up a fragment either by the given ID when inflated from XML or by the container ID when added in a FragmentTransaction. Here's an example:
Kotlin
supportFragmentManager.commit { supercede<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(truthful) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment Coffee
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .supersede(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentById(R.id.fragment_container);
Alternatively, you lot can assign a unique tag to a fragment and get a reference using findFragmentByTag(). You can assign a tag using the android:tag XML attribute on fragments that are divers within your layout, or during an add() or replace() operation inside a FragmentTransaction.
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container, "tag") setReorderingAllowed(true) addToBackStack(goose egg) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentByTag("tag") equally ExampleFragment Coffee
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .supplant(R.id.fragment_container, ExampleFragment.grade, cypher, "tag") .setReorderingAllowed(true) .addToBackStack(aught) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag"); Special considerations for child and sibling fragments
Only one FragmentManager is immune to control the fragment back stack at whatsoever given time. If your app shows multiple sibling fragments on the screen at the same time, or if your app uses child fragments, and then one FragmentManager must exist designated to handle your app's primary navigation.
To define the master navigation fragment within of a fragment transaction, call the setPrimaryNavigationFragment() method on the transaction, passing in the example of the fragment whose childFragmentManager should have primary control.
Consider the navigation construction as a series of layers, with the activeness every bit the outermost layer, wrapping each layer of child fragments underneath. Each layer must have a single primary navigation fragment. When the Back event occurs, the innermost layer controls navigation behavior. One time the innermost layer has no more fragment transactions from which to pop back, control returns to the side by side layer out, and this process repeats until y'all reach the activity.
Annotation that when ii or more fragments are displayed at the same time, only one of them tin exist the primary navigation fragment. Setting a fragment as the primary navigation fragment removes the designation from the previous fragment. Using the previous example, if yous set the detail fragment as the principal navigation fragment, the main fragment'due south designation is removed.
Support multiple dorsum stacks
In some cases, your app might need to back up multiple back stacks. A common example is if your app uses a bottom navigation bar. FragmentManager allows you to back up multiple dorsum stacks with the saveBackStack() and restoreBackStack() methods. These methods allow you to swap between back stacks by saving i back stack and restoring a different one.
saveBackStack() works similarly to calling popBackStack() with the optional proper name parameter: the specified transaction and all transactions later information technology on the stack are popped. The difference is that saveBackStack() saves the state of all fragments in the popped transactions.
For example, suppose you previously added a fragment to the back stack by committing a FragmentTransaction using addToBackStack():
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("replacement") } Java
supportFragmentManager.beginTransaction() .supervene upon(R.id.fragment_container, ExampleFragment.form, null) // setReorderingAllowed(true) and the optional cord statement for // addToBackStack() are both required if yous want to use saveBackStack(). .setReorderingAllowed(true) .addToBackStack("replacement") .commit(); In that case, you can save this fragment transaction and the state of ExampleFragment by calling saveState():
Kotlin
supportFragmentManager.saveBackStack("replacement") Coffee
supportFragmentManager.saveBackStack("replacement"); You can call restoreBackStack() with the aforementioned name parameter to restore all of the popped transactions and all of the saved fragment states:
Kotlin
supportFragmentManager.restoreBackStack("replacement") Java
supportFragmentManager.restoreBackStack("replacement"); Provide dependencies to your fragments
When adding a fragment, you can instantiate the fragment manually and add it to the FragmentTransaction.
Kotlin
fragmentManager.commit { // Instantiate a new instance before calculation val myFragment = ExampleFragment() add(R.id.fragment_view_container, myFragment) setReorderingAllowed(truthful) } Java
// Instantiate a new example earlier calculation ExampleFragment myFragment = new ExampleFragment(); fragmentManager.beginTransaction() .add together(R.id.fragment_view_container, myFragment) .setReorderingAllowed(true) .commit();
When you commit the fragment transaction, the instance of the fragment you lot created is the instance used. However, during a configuration change, your activity and all of its fragments are destroyed and and so recreated with the most applicable Android resources. The FragmentManager handles all of this for you. It recreates instances of your fragments, attaches them to the host, and recreates the back stack state.
By default, the FragmentManager uses a FragmentFactory that the framework provides to instantiate a new case of your fragment. This default mill uses reflection to notice and invoke a no-argument constructor for your fragment. This ways that yous can't use this default manufactory to provide dependencies to your fragment. It also means that whatever custom constructor you used to create your fragment the showtime time is non used during recreation by default.
To provide dependencies to your fragment, or to utilize any custom constructor, you must instead create a custom FragmentFactory bracket and then override FragmentFactory.instantiate. You can then override the FragmentManager's default factory with your custom factory, which is and then used to instantiate your fragments.
Suppose you have a DessertsFragment that is responsible for displaying popular desserts in your hometown. Allow's assume that DessertsFragment has a dependency on a DessertsRepository class that provides it with the information it needs to brandish the right UI to your user.
You might define your DessertsFragment to require a DessertsRepository case in its constructor.
Kotlin
course DessertsFragment(val dessertsRepository: DessertsRepository) : Fragment() { ... } Java
public class DessertsFragment extends Fragment { private DessertsRepository dessertsRepository; public DessertsFragment(DessertsRepository dessertsRepository) { super(); this.dessertsRepository = dessertsRepository; } // Getter omitted. ... } A unproblematic implementation of your FragmentFactory might await similar to the following.
Kotlin
class MyFragmentFactory(val repository: DessertsRepository) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when (loadFragmentClass(classLoader, className)) { DessertsFragment::grade.coffee -> 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 Cord className) { Class<? extends Fragment> fragmentClass = loadFragmentClass(classLoader, className); if (fragmentClass == DessertsFragment.course) { return new DessertsFragment(repository); } else { return super.instantiate(classLoader, className); } } } This example subclasses FragmentFactory, overriding the instantiate() method to provide custom fragment cosmos logic for a DessertsFragment. Other fragment classes are handled by the default behavior of FragmentFactory through super.instantiate().
Y'all tin can then designate MyFragmentFactory as the factory to utilize when constructing your app's fragments by setting a property on the FragmentManager. You must set this property prior to your activeness's super.onCreate() to ensure that MyFragmentFactory is used when recreating your fragments.
Kotlin
form MealActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MyFragmentFactory(DessertsRepository.getInstance()) super.onCreate(savedInstanceState) } } Java
public grade MealActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { DessertsRepository repository = DessertsRepository.getInstance(); getSupportFragmentManager().setFragmentFactory(new MyFragmentFactory(repository)); super.onCreate(savedInstanceState); } } Notation that setting the FragmentFactory in the activeness overrides fragment creation throughout the activity's fragments hierarchy. In other words, the childFragmentManager of whatever child fragments you add together uses the custom fragment manufactory set here unless overridden at a lower level.
Testing with FragmentFactory
In a single activity architecture, you should test your fragments in isolation using the FragmentScenario class. Since you cannot rely on the custom onCreate logic of your activity, you can instead laissez passer the FragmentFactory in as an argument to your fragments test, every bit shown in the post-obit instance:
// Inside your test val dessertRepository = mock(DessertsRepository::class.java) launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment { // Test Fragment logic } For detailed information about this testing process and for total examples, come across Test your app's Fragments.
Source: https://developer.android.com/guide/fragments/fragmentmanager
0 Response to "Execute Fragment Transaction Again on Pop"
Post a Comment