preface

Nice to meet you πŸ‘‹, I’m Flywith24.

The Fragment document has recently been rewritten by Android officials to accommodate the rapid evolution of best practices in 2020.

It is a good design, but it is always in a state of improvement. With the rapid update of AndroidX fragments, they are not what they used to be. There is still room for improvement (a single FragmentManager does not support multiple return stacks, and the Fragment itself and its view lifecycle are inconsistent). Considering the fact that there is a lot of new knowledge in this document and the extremely slow sinicization of official documents, this paper will translate the official documents of the Fragment version 2020 into Chinese. Those who like first-hand information can go straight to the official original. If you just want to focus on changes in a new document, click here. For space reasons, this document is divided into two parts.

Update your knowledge base to update your knowledge base

Update your knowledge base to update your knowledge base

This article will introduce the following:

  • The creation of a Fragment
  • Fragment manager
  • Fragment transaction
  • Fragments of animation
  • Fragment life cycle

The second part will introduce:

  • Fragment state is saved
  • Communication between the fragments
  • Fragment is used with AppBar
  • Display Dialog using DialogFragment
  • Fragments test


Check out the Easter eggs at πŸ˜‰

😝 Welcome to the Easter egg section, you must be a very curious little partner.

I’m an ocD sufferer, and I often insert code into text in the form of images to make it easier to read on mobile. But there is a problem: there is no way to copy code (this is important for CV developers 🀣).

One of the tricks I saw in the README document for a Github project the other day was to fold up long and somewhat intrusive sections so that the reader could unfold them as he chose.

This is how the egg is displayed. For the rest of this article, I will provide images and a copy of the source code, the latter of which is folded. You can click “Click to view code details” to expand the source code.

The egg is over. πŸ₯³

The overview

A Fragment represents a reusable portion of the developer’s APP UI. Fragments define and manage their own layout, have their own lifecycle, and can handle their own input events. Fragments cannot exist alone — they must have an activity or Fragment as host. The Fragment’s view tree is part of or attached to its host view tree.

🌟 Note: Some Android Jetpack libraries such as Navigation, BottomNavigationView, and ViewPager2 are designed to work with fragments.

modular

Fragments allow you to break up your UI into discrete chunks, thereby introducing modularity and reusability into your Activity UI. An Activity is an ideal place to place global elements of the APP UI, such as a navigation drawer. Fragments, on the other hand, are better suited for defining and managing the UI of the entire screen or parts of the screen.

Think about designing an app that works with all screen sizes. On larger screens, the app should be presented as a static navigation drawer and a grid list. On smaller screens, the app should display a BooTom Navigation bar and a linear list. Managing these changes in your activity can be cumbersome. Separating navigation elements from content makes this process more manageable. After that, the Activity is responsible for displaying the correct Navigation UI, and the Fragment is responsible for displaying the properly laid out list.

The image above shows two versions of the same interface. The large screen on the left contains a Navigation drawer controlled by the activity and a grid list controlled by the fragment. The small screen on the right contains a Bottom Navigation bar controlled by the activity and a linear list controlled by the fragment.

Splitting the UI into multiple fragments makes it easier to modify the appearance of an activity at run time. You can add/replace/remove fragments when your activity is in a STARTED Lifecycle State or higher. You can log these changes to a back stack managed by the activity to allow the previous state to be restored.

You can use the same Fragment instance in one activity, multiple activities, or even fragments (nested scenarios). With this in mind, you should provide only the logic needed to manage your own UI in your fragment and avoid relying on or operating on another fragment inside the fragment.

create

This section explains how to create a Fragment and add it to your activity.

Configure the environment

Fragment requires a dependency on the AndroidX Fragment Library. You need to add Google Maven Repository to your project’s build.gradle file

Click to see the code details
buildscript { ... repositories { google() ... } } allprojects { repositories { google() ... }}Copy the code

To add the AndroidX Fragment Library to your project, add the following dependencies to your app’s build.gradle file:

Click to see the code details
dependencies {
    def fragment_version = "1.2.5"

    // Use the Java language
    implementation "androidx.fragment:fragment:$fragment_version"
    // Kotlin language is used
    implementation "androidx.fragment:fragment-ktx:$fragment_version"
}
Copy the code

Create fragments class

Create a fragment, inherit AndroidX Fragment, override its methods and insert the app logic similar to how you create an Activity. To create a minimal fragment that defines its own layout, provide your fragment’s layout resources to the main constructor, as shown below:

Click to see the code details
// Kotlin
class ExampleFragment : Fragment(R.layout.example_fragment) / /Java
class ExampleFragment extends Fragment {
    public ExampleFragment(a) {
        super(R.layout.example_fragment); }}Copy the code

The Fragment library also provides several special purpose Fragment classes:

  • DialogFragment

    Display a floating dialog. Using this class to create a Dialog is a good alternative to using a Dialog in an Activity because the Fragment automatically handles dialog creation and removal.

  • PreferenceFragmentCompat

    Displays the hierarchy of a Preference object as a list. You can use PreferenceFragmentCompat to create a Settings interface for your app.

Add a Fragment to the activity

In general, your Fragment must be embedded in an AndroidX FragmentActivity to be part of the Activity Layout UI. FragmentActivity is a parent class to AppCompatActivity, so you don’t need to make any changes if you already inherit from AppCompatActivity.

There are two ways to add a fragment to an activity’s view tree:

  • Define the fragment in the Activity Layout file
  • Define the Fragment Container in the Activity Layout file and add the fragment to the activity later in code.

Either way, you need to add a FragmentContainerView to define where the fragment is in the Activity view tree. It is highly recommended that you always use FragmentContainerView as your Fragment container, because FragmentContainerView fixes some fragment bugs, Other ViewGroups (such as FrameLayout) do not provide fixes. [Fixed] FragmentContainerView FragmentContainerView [fixed]

Add fragments through XML

Use the FragmentContainerView tag to declare the fragment in the XML. Here is the activity layout file with a single FragmentContainerView:

Click to see the code details
<! -- res/layout/example_activity.xml -->
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.ExampleFragment" />
Copy the code

The Android :name attribute specifies the name of the Fragment class to be instantiated. When the layout of an activity is executed, the specified fragment is instantiated, and onInflate() is called in the new instantiated fragment, Create a FragmentTransaction to add the fragment to the FragmentManager.

🌟 Note: You can use class instead of Android :name to specify the fragment to be instantiated

Add fragments through code

To add a fragment to your activity layout programmatically, the layout should include a FragmentContainerView that acts as a container for the fragment, as in the following example:

Click to see the code details
<! -- res/layout/example_activity.xml -->
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
Copy the code

Unlike using XML, the FragmentContainerView does not use the Android: Name attribute, Instead of automatically instantiating a specific fragment, you instantiate a fragment in your code using FragmentTransaction and add it to the activity’s layout.

You can use fragment transactions to add, remove, and replace a fragment while the activity is running. In FragmentActivity, you can get an instance of FragmentManager, which can be used to create a FragmentTransaction. You can then instantiate the fragment using fragmentTransaction.add () in the activity’s onCreate() method, pass in the container’s ID and fragment class, and commit the transaction. Here is an example:

Click to see the code details
// Kotlin
class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Prevent system resource reclamation or configuration changes
        if (savedInstanceState == null) {
            supportFragmentManager.commit {
                setReorderingAllowed(true)
                add<ExampleFragment>(R.id.fragment_container_view)
            }
        }
    }
}

// Java
public class ExampleActivity extends AppCompatActivity {
    public ExampleActivity(a) {
        super(R.layout.example_activity);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
      	// Prevent system resource reclamation or configuration changes
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                .setReorderingAllowed(true)
                .add(R.id.fragment_container_view, ExampleFragment.class, null) .commit(); }}}Copy the code

🌟 Note: When executing FragmentTransaction, you should always use setReorderingAllowed(true). For more information on reordering transactions, see section Fragment Transactions.

In the previous example, the fragment transaction was created only if saveInstanceState was null. This is to ensure that the fragment is added only once when the activity is first created. When system resources are reclaimed or the configuration is changed, saveInstanceState is no longer null and you do not need to add the Fragment again, because the Fragment automatically recovers from savedInstanceState.

If the fragment needs to initialize data, you can pass parameters to the fragment by providing the Bundle in the fragmentTransaction.add () call as follows:

Click to see the code details
// Kotlin
class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState == null) {
            val bundle = bundleOf("some_int" to 0)
            supportFragmentManager.commit {
                setReorderingAllowed(true)
                add<ExampleFragment>(R.id.fragment_container_view, bundle)
            }
        }
    }
}

// Java
public class ExampleActivity extends AppCompatActivity {
    public ExampleActivity(a) {
        super(R.layout.example_activity);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            Bundle bundle = new Bundle();
            bundle.putInt("some_int".0);

            getSupportFragmentManager().beginTransaction()
                .setReorderingAllowed(true) .add(R.id.fragment_container_view, ExampleFragment.class, bundle) .commit(); }}}Copy the code

The Bundle can then be retrieved from the fragment by calling requireArguments(), and each parameter can be retrieved using the corresponding Bundle getter method:

Click to see the code details
// Kotlin
class ExampleFragment : Fragment(R.layout.example_fragment) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val someInt = requireArguments().getInt("some_int")... }}// Java
class ExampleFragment extends Fragment {
    public ExampleFragment(a) {
        super(R.layout.example_fragment);
    }

    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        int someInt = requireArguments().getInt("some_int"); . }}Copy the code

Fragment manager

🌟 Note: We strongly recommend using Navigation Library to manage your app’s Navigation. The framework follows best practices for handling fragments, returning stacks, and Fragment Managers. For more information on Navigation, see: Get started with the Navigation Component and Migrate to the Navigation Component.

FragmentManager is the class responsible for performing operations on app fragments, such as adding/removing/replacing fragments and adding those operations to the return stack.

If you use the Jetpack Navigation Library, you will probably never interact directly with the FragmentManager because it encapsulates the parts used by the FragmentManager. In other words, any app that uses fragments is using FragmentManager at some level, so it’s important to understand what it means and how it works.

This section describes how to access the FragmentManager, the FragmentManager role associated with activities and fragments, using the FragmentManager to manage the return stack, and providing data and dependencies for the fragment.

Visit FragmentManager

In the activity

Each FragmentActivity and its subclasses (e.g., AppCompatActivity) can all be getSupportFragmentManager FragmentManager () to visit

Access in the Fragment

A Fragment can also manage one or more sub-fragments. In the Fragment, you can get the FragmentManager instance that manages the child fragment by getChildFragmentManager(). To access the Fragment host’s FragmentManager, use getParentFragmentManager().

Let’s look at a few examples to show the relationship between fragments, their host, and the FragmentManager instance each fragment is associated with:

Green represents the host activity

Blue represents the host fragment

White represents child fragments

The figure above shows two examples, each with an activity host. In both cases, the host activity displays the top-level navigation to the user in the form of the BottomNavigationView, which is responsible for replacing the host fragment with a different interface in the app, each of which is a separate fragment.

The Host fragment in Example 1 manages two sub-fragments that form a separate left and right interface.

The Host fragment in Example 2 manages a separate sub-fragment that forms the fragment displayed in the sliding view.

Based on the above Settings, you can assume that each host is associated with a FragmentManager to manage its sub-fragments. The following illustration illustrates this:

Using the appropriate FragmentManager depends on where the caller is in the Fragment hierarchy and which fragment Manager you want to access.

Once you have a reference to the FragmentManager, you can use it to manipulate the fragment displayed to the user.

The child fragments

In general, your app should consist of one or a small number of activities, each representing a set of related interfaces. Activities may provide top-level navigation, view-state between viewModels and other fragments. Each individual destination in your app should be represented by a fragment.

If you want to display multiple fragments at once (such as ina split view or dashboard), use child fragments managed by destination Fragment and its childFragmentManager.

Other scenarios using sub-fragments might be:

  • Screen slides, the parent fragment manages a series of child fragments using ViewPager2
  • Subnavigation of a set of related interfaces
  • Jetpack Navigation uses sub-fragments as separate destinations. But when a user browses your app, an activity manages a separate parentNavHostFragmentAnd use different sub-Destination fragments to fill the parent’s location.

Using FragmentManager

The FragmentManager manages the fragment return stack. At run time, the FragmentManager can perform back-to-stack operations (such as adding/removing fragments in response to user actions). Each set of changes is committed together as a separate unit called FragmentTransaction. See the next section for a more in-depth discussion of transactions.

But when user press the return key on the device, or when the developers call FragmentManager. PopBackStack (), the most at the top of the fragment transaction will pop up from the stack. In other words, the transaction is cancelled. If there are no more fragment transactions in the stack and no sub-fragments are used, the return event is passed to the activity. If you are using child fragments, see the considerations for child and sibling fragments section.

When a transaction calls addToBackStack(), note that the transaction can contain any number of operations, such as adding multiple fragments, replacing fragments in multiple containers, and so on. When the return stack pops up, all of these operations are undone as individual atomic operations. If other transactions were committed prior to the popBackStack() call and addToBackStack() was not used on the transaction, these operations will not be undone. Therefore, in a FragmentTransaction, avoid mixing transactions that affect the return stack with transactions that do not.

Perform transactions

To display a fragment in a layout container, create a FragmentTransaction using the FragmentManager. Then, in a transaction, you can perform an add() or replace() operation on the container.

A simple transaction might look like this:

Click to see the code details
// 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 nulll
    .commit();
Copy the code

In the example above, ExampleFragment replaces the fragment (if any) in the layout container currently identified by the R.I.D.Fragment_container ID. Providing a replace() method to the Fragment class allows the FragmentManager to handle instantiation with its FragmentFactory. Refer to the next section for more information.

SetReorderingAllowed (true) Optimizes state changes of fragments involved in transactions so that animations and transitions work properly. For more information on navigation with animations and transitions, see the Fragment Transaction section and the Transition Animation section.

Calling addToBackStack() commits the transaction to the return stack. The user can later undo the transaction and return to the previous fragment using the install return button. If you add or remove multiple fragments in a single transaction, all of these operations will be undone when the return stack pops up. The optional name provided by addToBackStack() enables you to pop up this particular transaction using popBackStack().

If addToBackStack() is not called when removing the fragment transaction, the fragment removed after the transaction is committed will be destroyed and the user cannot navigate back to the fragment. If addToBackStack() is called while removing a fragment, the fragment is only in the STOPPED state and later in the RESUMED state when the user returns. Note that in this case the view has been destroyed. OnDestroyView () is executed but onDestroy() is not. For more information, see the lifecycle section.

Search for an existing fragment

You can use findFragmentById() to get an application to the current fragment in the layout container. Use findFragmentById() to find fragments with a given ID when added from an XML inflate or in a FragmentTransaction. Here is an example:

Click to see the code details
/ / πŸ‘‡ 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);Copy the code

In addition, you can assign a unique tag to the Fragment and use findFragmentByTag() to get a reference. You can use the Android: Tag XML attribute on a fragment defined within the layout or assign tags during an add() or replace() operation in FragmentTransaction.

Click to see the code details
/ / πŸ‘‡ 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");
Copy the code

Note for child fragment and brother Fragment

Only one FragmentManager is allowed to control the fragment return stack at any given time. If your app displays multiple sibling Fragments on the screen at the same time, or if your app uses sub-fragments, you must specify a FragmentManager to handle the app’s primary navigation.

In the affairs of the fragment defines the main navigation, please call on the transaction setPrimaryNavigationFragment () method and the incoming have childFragmentManager fragments of main control instance.

Think of the navigation structure as a series of layers, with the activity as the outermost layer, wrapping each sub-fragment layer underneath. Each layer must have a main navigation fragment. But when a return event occurs, the innermost layer controls the navigation behavior, and once the innermost layer has no more transactions to pop from the return stack, control goes back to the next layer, and the process is repeated until the event reaches the activity.

Note that when two or more fragments are displayed at the same time, only one of them can be the dominant navigation fragment. If you set the fragment to the main navigation, the previous fragment for which the screen designed should be deleted. Using the example above, if you set the detail fragment as the main navigation fragment, the main fragment for which the screen designed should be removed.

Provide fragment dependencies

When adding a fragment, you can manually instantiate the fragment and add it to a FragmentTransaction.

Click to see the code details
/ / πŸ‘‡ Kotlin
fragmentManager.commit {
    // Instantiate before add
    val myFragment = ExampleFragment()
    add(R.id.fragment_view_container, myFragment)
    setReorderingAllowed(true)}/ / πŸ‘‡ Java
// Instantiate before add
ExampleFragment myFragment = new ExampleFragment();
fragmentManager.beginTransaction()
    .add(R.id.fragment_view_container, myFragment)
    .setReorderingAllowed(true)
    .commit();
Copy the code

When submitting a Fragment transaction, the fragment instance created is the instance used. However, when system resources are recycled or configuration changes, your activity and all its fragments are destroyed and recreated using the most appropriate Android resources. The FragmentManager handles all of this for you. It recreates the fragment instance, then attaches it to the host and recreates the return stack.

By default, the FragmentManager instantiates a new instance of the fragment using the FragmentFactory provided by the framework. This default factory uses reflection to find and call the no-argument constructor for the fragment. This means that you cannot use this default factory to provide a dependency on fragments. This also means that by default, any custom constructors you used to create a fragment for the first time will not be used during the re-creation process.

To provide for fragments of dependencies or use any custom constructor, you must create FragmentFactory subclasses, and then rewrite FragmentFactory. Instantiate. You can then override the default FragmentManager factory using a custom factory, which you can then use to instantiate the Fragment.

Let’s say you have a DessertsFragment who is in charge of presenting a popular dessert in your home town. Assume that DessertsFragment relies on the DessertsRepository class, which provides it with the information it needs to display the correct UI to the user.

You might define the requested DessertsRepository instance in the DessertsFragment constructor:

Click to see the code details
/ / πŸ‘‡ Kotlin
class DessertsFragment(val dessertsRepository: DessertsRepository) : Fragment(a){... }/ / πŸ‘‡ Java
public class DessertsFragment extends Fragment {
    private DessertsRepository dessertsRepository;

    public DessertsFragment(DessertsRepository dessertsRepository) {
        super(a);this.dessertsRepository = dessertsRepository; }... }Copy the code

A simple implementation of FragmentFactory might look something like this:

Click to see the code details
/ / πŸ‘‡ Kotlin
class MyFragmentFactory(val repository: DessertsRepository) : FragmentFactory(a){
    override fun instantiate(classLoader: ClassLoader, className: String): Fragment =
            when (loadFragmentClass(classLoader, className)) {
      																	//πŸ‘‡ changed the default implementation here
                DessertsFragment::class.java -> DessertsFragment(repository)
                else -> super.instantiate(classLoader, className)
            }
}


/ / πŸ‘‡ Java
public class MyFragmentFactory extends FragmentFactory {
    private DessertsRepository repository;

    public MyFragmentFactory(DessertsRepository repository) {
        super(a);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) {
          	//πŸ‘‡ changed the default implementation here
            return new DessertsFragment(repository);
        } else {
            return super.instantiate(classLoader, className); }}}Copy the code

The above example creates a subclass of FragmentFactory, MyFragmentFactory. This class overrides the instantiate() method to provide custom fragment creation logic for DessertsFragment. Other fragments are handled by the default behavior of FragmentFactory via super.instantiat().

You can then specify MyFragmentFactory as the factory to use when constructing your App Fragment by setting a property on the FragmentManager. You must set this property before the activity’s super.onCreate() to ensure that MyFragmentFactory is used when recreating the fragment.

Click to see the code details
/ / πŸ‘‡ Kotlin
class MealActivity : AppCompatActivity(a){
    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); }}Copy the code

Note that setting the FragmentFactory in the Activity overrides the fragment creation in the entire Activity’s Fragment hierarchy. In other words, the childFragmentManager of any child fragment you add uses the custom Fragment factory set here, unless it is overridden at a lower level.

Use FragmentFactory tests

In a single activity architecture, FragmentScenario should be used to test fragments in isolation. Since you can’t rely on the activity’s custom onCreate logic, you can pass FragmentFactory as a parameter to the Fragment test, as follows:

Click to see the code details
// Test the internal
val dessertRepository = mock(DessertsRepository::class.java)
launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment {
    // Test the Fragment logic
}
Copy the code

For more information about the testing process and a complete example, refer to the Testing section.

The transaction

At run time, the FragmentManager can add, remove, and replace fragments and perform other operations in response to user interactions. Each set of changes to the Fragment submitted by the developer is called a “transaction”. You can use FragmentTransaction to provide apis that specify operations to be performed within a transaction, and you can group multiple operations into a single transaction. For example, a transaction can add or replace multiple fragments. This is useful when you have multiple fragments of the same class on the same screen.

You can save each transaction to a return stack managed by the FragmentManager, allowing the user to return to the state of the last fragment, just as an activity can return to a previous activity.

You can get an instance of FragmentTransaction by calling the beginTransaction() method of the FragmentManager, as in the following example:

Click to see the code details
/ / πŸ‘‡ Kotlin
val fragmentManager = ...
val fragmentTransaction = fragmentManager.beginTransaction()

/ / πŸ‘‡ Java
FragmentManager fragmentManager = ...
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Copy the code

Each FragmentTransaction must commit a transaction at the end. The commit() method signals to the FragmentManager that all operations have been added to the transaction.

Click to see the code details
/ / πŸ‘‡ Kotlin
val fragmentManager = ...
// This extension function is provided by fragment-ktx
// Automatically start and commit transactions
fragmentManager.commit {
    // Add operations here
}

/ / πŸ‘‡ Java
FragmentManager fragmentManager = ...
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

// Add operations here

fragmentTransaction.commit();
Copy the code

Allows reordering of fragment state changes

Each FragmentTransaction should use setReorderingAllowed(true) :

Click to see the code details
/ / πŸ‘‡ Kotlin
supportFragmentManager.commit {
    ...
    setReorderingAllowed(true)}/ / πŸ‘‡ Java
FragmentManager fragmentManager = ...
fragmentManager.beginTransaction()
    ...
    .setReorderingAllowed(true)
    .commit();
Copy the code

For compatibility, resort is disabled by default. However, if you need to allow the FragmentManager to execute correctly on the return stack and run animations and transitions, enabling reordering ensures that multiple transactions are executed together, Any intermediate fragment (such as one that is added and replaced immediately) will not undergo life-cycle changes or perform other animations or transitions. Note that reordering affects the start and undo of transactions.

Add/Remove Fragments

To add the fragment to the FragmentManager, call the Add () method on the transaction. This method receives the ID of the fragment container and the name of the class to which the fragment is to be added. The added fragment will move to the RESUMED state. FragmentContainerView is strongly recommended for containers.

To remove the fragment from the host, call the remove() method and pass in an instance of the fragment. The Fragment instance is retrieved from the Fragment Manager using either findFragmentById() or findFragmentByTag. If the fragment has been added to the container, its view is removed from the container. The removed fragment is moved to the DESTROYED state.

Replace () replaces the existing fragment in the container with a new fragment instance. Calling replace() is equivalent to calling remove() on a fragment in a container and adding a new fragment to the same container.

The following code shows how to replace one fragment with another:

Click to see the code details
/ / πŸ‘‡ Kotlin
val fragmentManager = // ...

fragmentManager.commit {
    setReorderingAllowed(true)

    replace<ExampleFragment>(R.id.fragment_container)
}


/ / πŸ‘‡ Java
FragmentManager fragmentManager = ...
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setReorderingAllowed(true);

transaction.replace(R.id.fragment_container, ExampleFragment.class, null);

transaction.commit();
Copy the code

In the example above, a new instance of ExampleFragment would replace the fragment (if any) of the layout container currently identified by R.i.D.Fragment_container.

🌟 Note: It is highly recommended to use a Class instead of a fragment instance when operating on a fragment to ensure that the same mechanism is used when the fragment is restored through saved State. See the Fragment Manager section for more details.

By default, changes made in FragmentTransaction are not added to the return stack. To save these changes, call the addToBackStack() method on FragmentTransaction. Refer to the Fragment Manager section for more information.

Asynchronous submission

Calling commit() does not immediately execute the transaction, which is scheduled to run on the main thread as soon as possible. Call commitNow() to run the fragment transaction on the main thread immediately if necessary.

Note that commitNow is not compatible with addToBackStack. But you can call executePendingTransactions () has to invoke the commit () method but didn’t running transactions, the method is compatible with addToBackStack.

For most scenarios, use commit().

The order of operations is important

The order in which actions are performed in FragmentTransaction is important, especially when using the setCustomAnimations() method. This method applies the given animation to all subsequent fragment operations.

Click to see the code details
/ / πŸ‘‡ Kotlin
supportFragmentManager.commit {
    setCustomAnimations(enter1, exit1, popEnter1, popExit1)
    add<ExampleFragment>(R.id.container) // The first animation
    setCustomAnimations(enter2, exit2, popEnter2, popExit2)
    add<ExampleFragment>(R.id.container) // Second animation
}


/ / πŸ‘‡ Java
getSupportFragmentManager().beginTransaction()
        .setCustomAnimations(enter1, exit1, popEnter1, popExit1)
        .add(R.id.container, ExampleFragment.class, null) // The first animation
        .setCustomAnimations(enter2, exit2, popEnter2, popExit2)
        .add(R.id.container, ExampleFragment.class, null) // Second animation
        .commit()
Copy the code

Limit the fragment’s life cycle

FragmentTransaction can affect the life cycle state of individual fragments added within the scope of a transaction. When creating a FragmentTransaction, the setMaxLifecycle() method can set the maximum state for a given fragment. For example, ViewPager2 uses the setMaxLifecycle() method to restrict off-screen fragments to a STARTED state.

Show and hide fragment views

Use the show() and hide() methods of FragmentTransaction to show and hide views of fragments that have been added to the container. These methods set the visibility of the Fragment View without affecting the lifetime of the fragment.

Although you do not need to use a Fragment transaction to toggle the visibility of a Fragment View, these methods can be useful in scenarios where the visibility of a transaction returned on the stack is changed.

Connect and separate fragments

FragmentTransaction’s detach() method separates the fragment from the UI, destroying the fragment’s view. The fragment remains in the same state (STOPPED) as in the return stack. This means that the fragment has been removed from the UI, but is still managed by the Fragment Manager.

The attach() method reconnects the previously separated fragment. This causes its view tree to be recreated, added to the UI and displayed.

Because FragmentTransaction is treated as a single set of atomic operations, calls to separate and connect to the same Fragment instance cancel each other out in the same transaction, avoiding destruction and immediate rebuilding of the Fragment UI. If you want to separate the fragment and then reconnect it immediately, use a separate transaction, or use the executePendingOperations() method if you commit the transaction using commit().

🌟 Note: The attach() and detach() methods are independent of the Fragment’s onAttach() and onDetach() methods. For more information on these Fragment methods, see the life cycle section.

Animated transitions

The Fragment API provides two switching effects for connecting Fragment navigation. One is the Animation framework, which includes Animation and Animator. The other is the Transition framework, which contains transformations of shared elements.

🌟 Note: In this section, we use animation to describe the effects in the animation framework and transition to describe the effects in the transition framework. The two frameworks are mutually exclusive and should not be used together.

You can think of custom effects as a transition between entering and exiting a fragment and elements shared by the fragment.

  • Enter effects define how the fragment enters the screen. For example, you can create an effect that slides in from the edge of the screen as the fragment enters.
  • Exit effects define how the fragment exits the screen. For example, you can create a fragment that fades out when it leaves.
  • Shared element transitions define how views shared between two fragments move between them. If B becomes visible, the image displayed in fragment A’s ImageView will be converted to Fragment B.

Set the animation

First, you need to create animations for entry and exit effects that will run when navigating to the new fragment. You can define an animation as a tween animation resource. These resources allow you to define how the fragment should rotate, stretch, fade in and out, and move during animation. For example, you might want the current fragment to fade out and the new fragment to slide in from the right edge of the screen, as shown below:

These animations can be defined in the RES/ANim directory:

Click to see the code details
<! -- res/anim/fade_out.xml -->

       
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromAlpha="1"
    android:toAlpha="0" />

<! -- res/anim/slide_in.xml -->

       
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromXDelta="100%"
    android:toXDelta="0%" />
Copy the code

🌟 Note: It is strongly recommended to use Transition for effects involving multiple animation types, due to known problems with nested animationSets.

You can also specify animations for the entry and exit effects that run when the return stack is popped. These are called popEnter and popExit animations. For example, when the user jumps back to the previous screen, you might want the current fragment to slide out of the right edge of the screen while the previous fragment fades in:

These animations can be defined as follows:

Click to see the code details
<! -- res/anim/slide_out.xml -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromXDelta="0%"
    android:toXDelta="100%" />

<! -- res/anim/fade_in.xml -->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:interpolator="@android:anim/decelerate_interpolator"
    android:fromAlpha="0"
    android:toAlpha="1" />
Copy the code

. After the definition animation, by calling the FragmentTransaction setCustomAnimations () to use them and passed on its resource id animation resources, the following sample:

Click to see the code details
/ / πŸ‘‡ Kotlin
val fragment = FragmentB()
supportFragmentManager.commit {
    setCustomAnimations(
        enter = R.anim.slide_in,
        exit = R.anim.fade_out,
        popEnter = R.anim.fade_in,
        popExit = R.anim.slide_out
    )
    replace(R.id.fragment_container, fragment)
    addToBackStack(null)}/ / πŸ‘‡ Java
Fragment fragment = new FragmentB();
getSupportFragmentManager().beginTransaction()
    .setCustomAnimations(
        R.anim.slide_in,  // enter
        R.anim.fade_out,  // exit
        R.anim.fade_in,   // popEnter
        R.anim.slide_out  // popExit
    )
    .replace(R.id.fragment_container, fragment)
    .addToBackStack(null)
    .commit();
Copy the code

🌟 note: FragmentTransaction. SetCustomAnimations () is applied to the custom animation FragmentTransaction all future fragments in the operation. Previous operations in the transaction are not affected.

Set the transition

You can also use transition to define entry and exit effects. You can define these transitions in an XML resource file. For example, you might want the current fragment to fade out and the new fragment to slide in from the right edge of the screen. These transitions can be defined as follows:

Click to see the code details
<! -- res/transition/fade.xml -->
<fade xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"/>

<! -- res/transition/slide_right.xml -->
<slide xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@android:integer/config_shortAnimTime"
    android:slideEdge="right" />
Copy the code

Now that I’ve defined transition, Apply transition and pass the resource through its resource ID by calling setEnterTransition() on entering the fragment and setExitTransition() on exiting the fragment, as shown below:

Click to see the code details
/ / πŸ‘‡ Kotlin
class FragmentA : Fragment(a){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val inflater = TransitionInflater.from(requireContext())
        exitTransition = inflater.inflateTransition(R.transition.fade)
    }
}

class FragmentB : Fragment(a){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val inflater = TransitionInflater.from(requireContext())
        enterTransition = inflater.inflateTransition(R.transition.slide_right)
    }
}

/ / πŸ‘‡ Java
public class FragmentA extends Fragment {
    @Override
    public View onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); TransitionInflater inflater = TransitionInflater.from(requireContext()); setExitTransition(inflater.inflateTransition(R.transition.fade)); }}public class FragmentB extends Fragment {
    @Override
    public View onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); TransitionInflater inflater = TransitionInflater.from(requireContext()); setEnterTransition(inflater.inflateTransition(R.transition.slide_right)); }}Copy the code

Fragment supports AndroidX Transitions. While Fragments also support Framework Transitions, we strongly recommend using AndroidX Transitions because it supports API 14 and above and includes some bug fixes.

Transitions using shared elements

As part of the Transition framework, shared element transitions determine how views move between two fragments during a fragment switch. For example, you might want the image displayed in Fragment A’s ImageView to transition to Fragment B once B becomes visible. The diagram below:

Here is the fragment switch using shared elements:

  1. Assign a unique transition name to each shared element view
  2. Add the shared element view and transition name to FragmentTransaction
  3. Set the shared element transition animation

First, you must assign a unique transition name to each shared element view to allow the view to be mapped from one fragment to the next. Use ViewCompat. SetTransitionName () on each Shared fragments in the layout element on the name of the transition, the method can be compatible with the API 14 and older version. For example, the transition from the ImageView fragment A and Fragment B can be split like this:

Click to see the code details
/ / πŸ‘‡ Kotlin
class FragmentA : Fragment(a){
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {... val itemImageView = view.findViewById<ImageView>(R.id.item_image) ViewCompat.setTransitionName(itemImageView, "Item_image)}}"class FragmentB : Fragment(a){
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {... val heroImageView = view.findViewById<ImageView>(R.id.hero_image) ViewCompat.setTransitionName(heroImageView, "Hero_image)}}"/ / πŸ‘‡ Java
public class FragmentA extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        ...
        ImageView itemImageView = view.findViewById(R.id.item_image);
        ViewCompat.setTransitionName(itemImageView, β€œitem_image”);
    }
}

public class FragmentB extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        ...
        ImageView heroImageView = view.findViewById(R.id.hero_image);
        ViewCompat.setTransitionName(heroImageView, β€œhero_image”);
    }
}
Copy the code

🌟 Note: For minSdkVersion API 21 or later apps, you can optionally assign the transition name to a specific view using the Android :transitionName attribute in the XML layout.

To add shared elements to a Fragment switch, your FragmentTransaction must know how the view of each shared element maps from one fragment to the next. By calling the FragmentTransaction. AddSharedElement () will each Shared elements added to the FragmentTransaction, in the next fragment into view and the corresponding view name of the transition, as follows:

Click to see the code details
/ / πŸ‘‡ Kotlinval fragment = FragmentB() supportFragmentManager.commit { setCustomAnimations(...) AddSharedElement (itemImageView, "Hero_image") replace(R.i.fragment_container, fragment) addToBackStack(itemImageView, "Hero_image") replace(R.I.fragment_container, fragment) addToBackStack(null)}/ / πŸ‘‡ Java
Fragment fragment = newFragmentB(); getSupportFragmentManager().beginTransaction() .setCustomAnimations(...) .addShareDelement (itemImageView, "Hero_image").replace(R.i.Fragment_container, fragment).addToBackStack(.addShareDelement (itemImageView, "Hero_image").replace(R.I.fragment_container, fragment).addToBackStack(null)
    .commit();
Copy the code

To specify how shared elements transition from one fragment to the next, you must set the Enter Transition on the fragment to navigate to. Call the fragments of the onCreate () method of fragments. SetSharedElementEnterTransition (), as follows:

Click to see the code details
/ / πŸ‘‡ Kotlin
class FragmentB : Fragment(a){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sharedElementEnterTransition = TransitionInflater.from(requireContext())
             .inflateTransition(R.transition.shared_image)
    }
}

/ / πŸ‘‡ Java
public class FragmentB extends Fragment {
    @Override
    public View onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); Transition transition = TransitionInflater.from(requireContext()) .inflateTransition(R.transition.shared_image); setSharedElementEnterTransition(transition); }}Copy the code

Shared_image Transition is defined as follows:

Click to see the code details
<! -- res/transition/shared_image.xml -->
<transitionSet>
    <changeImageTransform />
</transitionSet>
Copy the code

All Transition subclasses support transitions to shared elements. If you want to create a custom Transition, see Creating custom Transitions. The changeImageTransform used in the previous example is a defined transform. You can find other Transition subclasses in the Transition API documentation.

By default, the Enter transition for shared elements is also used as the return transition for shared elements. Return Transition determines how shared elements are converted back to the previous fragment when a fragment transaction is ejected from the return stack. If you want to specify other return conversion, you can in the fragments of the onCreate () method is used in fragments. SetSharedElementReturnTransition () to specify.

Delay the transition

In some cases, you may need to delay fragment conversion for a short period of time. For example, you might have to wait until all views in an incoming fragment have been measured and laid out before the system can accurately capture the beginning and end states of their transitions.

In addition, your transition may need to be delayed until some necessary data is loaded. For example, you need to wait until an image is added to the shared element, otherwise the transition may be disrupted if the image is loaded during or after the transition.

To delay the transition, you must ensure that the fragment transaction allows reordering of fragment state changes. Please call FragmentTransaction. SetReorderingAllowed (), as follows:

Translates to delay, into fragments onViewCreated () method call fragments. PostponeEnterTransition ().

Load data and ready to start after conversion, please call fragments. StartPostponedEnterTransition () method. The following example uses Glide to load an image into a shared ImageView and delays the transition until the image is loaded.

When dealing with slow network speeds, you may want to delay the transition for a certain amount of time rather than wait for all data to load before starting. In this scenario, you can instead into fragments of onViewCreated fragments () method call. PostponeEnterTransition (long, TimeUnit) and incoming duration and time unit. After a specified time, the transition automatically begins.

Use shared element transitions in RecyclerView

A delayed Enter transition should not begin until all views entering the fragment have been measured and laid out. When using RecyclerView, you must wait for any data to load and be ready to draw the RecyclerView item before starting transition. Here’s an example:

Click to see the code details
/ / πŸ‘‡ Kotlin
class FragmentA : Fragment(a){
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        postponeEnterTransition()

        // Wait for data to load
        viewModel.data.observe(viewLifecycleOwner) {
            // Set the data in RecyclerView adapter
            adapter.setData(it)
            // Start the transition after all the views are measured and laid out(view.parent as? ViewGroup)? .doOnPreDraw { startPostponedEnterTransition() } } } }/ / πŸ‘‡ Java
public class FragmentA extends Fragment {
    @Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        postponeEnterTransition();

        final ViewGroup parentView = (ViewGroup) view.getParent();
        // Wait for data to load
        viewModel.getData()
            .observe(getViewLifecycleOwner(), new Observer<List<String>>() {
                @Override
                public void onChanged(List<String> list) {
                    // Set the data in RecyclerView adapter
                    adapter.setData(it);
                    // Start the transition after all the views are measured and laid out
                    parentView.getViewTreeObserver()
                        .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                            parentView.getViewTreeObserver()
                                .removeOnPreDrawListener(this);
                            startPostponedEnterTransition();
                            return true; }); }}); }}Copy the code

Please note that in fragments on the parent view sets the ViewTreeObserver. OnPreDrawListener. This is to ensure that all fragment views are measured, laid out and ready to be drawn before you start delaying the Enter transition.

🌟 note: When transitioning from a fragment that uses RecyclerView to use shared elements to another fragment, you still have to use RecyclerView to delay that fragment, To ensure that the returned shared element transition works when the RecyclerView is popped up.

Another point to consider when using shared element transformations with RecyclerView is that since any number of items share the layout, you cannot set the transition name in the XML layout of the RecyclerView Item. You must assign a unique transition name so that the transition animation uses the correct view.

By binding ViewHolder, you can give each item’s shared elements a unique transition name. For example, if the data for each item contains a unique ID, it can be used as the transition name, as in the following example:

Click to see the code details
/ / πŸ‘‡ Kotlin
class ExampleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val image = itemView.findViewById<ImageView>(R.id.item_image)

    fun bind(id: String) { ViewCompat.setTransitionName(image, id) ... }}/ / πŸ‘‡ Java
public class ExampleViewHolder extends RecyclerView.ViewHolder {
    private final ImageView image;

    ExampleViewHolder(View itemView) {
        super(itemView);
        image = itemView.findViewById(R.id.item_image);
    }

    public void bind(String id) { ViewCompat.setTransitionName(image, id); . }}Copy the code

Additional resources

For more information on fragment transitions, see other resources below.

The sample

  • Android Fragment Transitions: RecyclerView to ViewPager Sample

blog

  • Continuous Shared Element Transitions: RecyclerView to ViewPager
  • Fragment Transitions

The life cycle

Each Fragment instance has its own life cycle. As users navigate and interact with your app (such as adding, removing, and entering and exiting the screen), your Fragment transitions between states throughout its life cycle.

To manage the Lifecycle, the Fragment implements LifecycleOwner, exposing that Lifecycle objects can be obtained through the getLifecycle() method.

Each possible Lifecycle State is represented in the Lifecycle.State enumeration:

  • INITIALIZED
  • CREATED
  • STARTED
  • RESUMED
  • DESTROYED

By building fragments on top of Lifecycle, you can handle the Lifecycle with life-aware components. For example, you can use a lifecycle aware component to display the location of a device on the screen. This component can automatically start listening when the Fragment becomes active and stop when the fragment becomes inactive.

As an alternative to using LifecycleObserver, the Fragment class includes callback methods corresponding to each change in the Fragment lifecycle. These methods are: onCreate(), onStart(), onResume(), onPause(), onStop() and onDestroy().

Fragment views have a separate life cycle that is managed independently of the fragment’s life cycle. Fragments of view maintains a LifecycleOwner, it can getViewLifecycleOwner () or getViewLifecycleOwnerLiveData () to access it. Having access to the view’s lifecycle is useful for lifecycle aware components that perform work only when the Fragment View exists, such as observing that LiveData only performs work when it is displayed on the screen.

The fragment lifecycle, discussed in detail in this section, explains some of the rules for determining the fragment lifecycle state and shows the relationship between the lifecycle state and the fragment lifecycle callback.

Fragment and fragments of the manager

When the fragment is instantiated, it starts with the INITIALIZED state. In order for a fragment to switch throughout its life, it must be added to the FragmentManager. The FragmentManager is responsible for determining what state its fragment should be in and then moving it into that state.

Outside of the Fragment life cycle, the FragmentManager is also responsible for attaching the fragment to its host activity and detach it when it is no longer in use. The Fragment class has two callback methods, onAttach() and onDetach(), and when either of these events occurs, you can override them to execute your own logic.

When you add a fragment to the FragmentManager and attach it to its host activity, the onAttach() callback is called and the fragment is active, And the FragmentManager is managing its life cycle state. At this point, the FragmentManager method (such as findFragmentById()) returns the fragment.

The onAttach() method is always called before any life cycle state changes.

The onDetach() callback is called when the Fragment has been removed from the FragmentManager and its host Activity Detach. The Fragment is no longer active and can no longer be retrieved using findFragmentById().

The onDetach() method is always called after any lifecycle state changes.

Note that these callbacks are independent of the attach() and detach() methods of FragmentTransaction. For more information on these methods, see the Fragment Transaction section.

⚠️ warning: Avoid reusing Fragment instances after removing them from FragmentManager. As the Fragment processes its own internal state cleanup, you may inadvertently transfer your state to a reused instance.

Fragment life cycle state and callback

When determining the fragment’s life-cycle state, the FragmentManager considers the following factors:

  • The maximum state of a fragment is determined by its FragmentManager. The fragment cannot go beyond its FragmentManager state
  • As part of FragmentTransaction, you can use setMaxLifecycle() to set the maximum lifecycle state on the fragment
  • The fragment’s life-cycle state can never be larger than its host. For example, the parent fragment or activity must start before its child fragment. Similarly, a child fragment must be stopped before its parent fragment or activity

⚠️ Warning: Avoid adding fragments in XML using the

tag. Because the

tag allows the fragment to move out of its FragmentManager state. Always use FragmentContainerView to add fragments through XML.

The figure above shows the life cycle state of each fragment and its relationship to the fragment’s life cycle callback and view life cycle of the fragment.

As the fragment switches through its life cycle, it moves up and down between its states. For example, a fragment added to the top of the return stack moves up from CREATED to STARTED to RESUMED. In contrast, when a fragment is ejected from the return stack, it moves down through those states, from RESUMED to STARTED to CREATED, and finally to DESTROYED.

An upward transition of state

When moving up its lifecycle state, the Fragment first invokes the associated lifecycle callback for its new state. Once the callback is complete, the associated Lifecycle.Event is issued to the observer and then followed by the Fragment’s View Lifecycle (if instantiated).

Fragment CREATED

When your Fragment reaches the CREATED state, it has been added to the FragmentManager and the onAttach() method has been called.

This would be the appropriate place to restore all the saved states associated with the fragment itself via the fragment’s SavedStateRegistry. Note that the View of the Fragment has not been created at this point, and any state associated with the view of the fragment should be restored only after the view is created.

This procedure calls the onCreate() callback. The callback will also receive a saveInstanceStateState Bundle parameter that contains the state previously saved by onSaveInstanceState(). Note that the value of savedInstanceState is null the first time the Fragment is created, but it is always non-null for subsequent recreations even if onSaveInstanceState is not overridden. For more details, see the making State Save section.

Fragment CREATED and View INITIALIZED

Create the View lifecycle of your fragment only if your fragment provides a valid View instance. In most cases, you can use a fragment constructor with @layoutid that automatically inflates the view at the appropriate time. You can also override onCreateView() to programmatically inflate or create a view with a fragment.

A view of the Fragment is set on the fragment if and only if it is instantiated with a non-null view and can be retrieved using getView(). Then, using fragments view corresponds with the new the INITIALIZED LifecycleOwner update getViewLifecycleOwnerLiveData (). The onViewCreated() lifecycle callback is also called at this point.

This is the place to set the view’s initial state and start looking at the appropriate place to set adapter on any RecyclerView or ViewPager2 instances in the Fragment view and its callback to update the LiveData instance of the Fragment view.

Fragments, and the View is CREATED

After the fragment view is CREATED, it restores the previous view state (if any), and then moves the view lifecycle to the CREATED state. The view lifecycle owner also issues an ON_CREATE event to its observer. Here, you should restore all other states associated with the Fragment view.

This procedure also calls the onViewStateRestored() callback.

Fragments, and the View of STARTED

It is highly recommended to bind life-cycle supporting components to the fragment’s STARTED state because this state ensures that the fragment’s view is available (if it has been created), And you can safely perform FragmentTransaction on the Fragment’s child FragmentManager. If the fragment view is not null, the fragment view Lifecycle will be moved to the STARTED immediately after the fragment life is moved to the STARTED view.

When the fragment becomes STARTED, the onStart() callback is called.

🌟 Note: Components such as ViewPager2 set the maximum life cycle of the off-screen fragment to STARTED

Fragments and View RESUMED

When the fragment is visible, all Animator and Transition effects are complete, and the fragment is ready to interact with the user. The fragment’s life cycle moves to the RESUMED state and calls the onResume() callback.

Switching to RESUMED state is the state that indicates that the user is now ready to interact with your fragment. An unresumed fragment should not manually set the focus of the view or try to handle the visibility of the input method.

A downward transition of state

When the fragment moves down to a lower Lifecycle state, the associated Lifecycle.Event will be sent to the observer through the fragment’s view Lifecycle (if instantiated), followed by the Fragment’s Lifecycle. After a life-cycle event for the fragment is emitted, the fragment invokes the associated life-cycle callback.

Fragments, and the View of STARTED

As the user starts to leave the fragment and while the fragment is still visible, the fragment and its view will move back to the STARTED state and issue an ON_PAUSE event to its observer. The Fragment then invokes its onPause() callback.

Fragments, and the View is CREATED

Once the fragment is no longer visible, the lifecycle of the fragment and its view goes into an CREATED state and an ON_STOP event is issued to its observer. State transitions are triggered not only by the parent activity or fragment stopping, but also by the parent activity or fragment saving the state. This behavior ensures that the ON_STOP event is called before the fragment state is saved. This makes the ON_STOP event the last place to safely execute a FragmentTransaction on a child FragmentManager.

As shown in the figure above, the sequence of onStop() callbacks and the process of saving state using onSaveInstanceState() varies depending on the API level. For all versions of API prior to 28, onSaveInstanceState() is called before onStop(). For API level 28 and later, the order of calls is reversed.

Fragment CREATED and View DESTROYED

After all exit animations and transitions are complete and the Fragment view has detach from the window, the Fragment’s view Lifecycle moves to the DESTROYED state and issues an ON_DESTROY event to its observer. The Fragment then calls its onDestroyView() callback. At this point, the fragments of the view has reached, at the end of its life cycle and getViewLifecycleOwnerLiveData () returns null.

At this point, all references to the Fragment view should be removed so that the fragment view can be garbage collected.

Fragment DESTROYED

If the Fragment is removed or the FragmentManager is DESTROYED, the fragment’s lifecycle goes into a DESTROYED state and the ON_DESTROY event is sent to its observer. The Fragment then calls its onDestroy() callback. At this point, the fragment has reached the end of its life cycle.

Additional resources

For more information about the Fragment life cycle, see other resources below.

  • Handling Lifecycles with Lifecycle-Aware Components
  • Fragments: Rebuilding the Internals

About me

I’m Flywith24, an Android App/Rom developer. Currently, I focus on writing systematic articles about Android.

The Android Detail column is being updated for those who want to build a systematic knowledge system. All of my blog content has been organized here, click on Watch in the upper right corner to get my posts updated at πŸ˜‰

  • The Denver nuggets
  • Small column
  • Github
  • WeChat: Flywith24