Adding a UI Fragment to the FragmentManager

When the Fragment class was introduced in Honeycomb, the Activity class was changed to include a piece called the FragmentManager. The FragmentManager is responsible for managing your fragments and adding their views to the activity’s view hierarchy (Figure 7.18).

The FragmentManager handles two things: a list of fragments and a back stack of fragment transactions (which you will learn about shortly).

Figure 7.18  The FragmentManager

Illustration shows fragment manager.

For CriminalIntent, you will only be concerned with the FragmentManager’s list of fragments.

To add a fragment to an activity in code, you make explicit calls to the activity’s FragmentManager. The first step is to get the FragmentManager itself. Do so in onCreate(Bundle) in CrimeActivity.java.

Listing 7.15  Getting the FragmentManager (CrimeActivity.java)

public class CrimeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crime);

        FragmentManager fm = getSupportFragmentManager();
    }
}

If you see an error after adding this line of code, check the import statements to make sure that the support version of the FragmentManager class was imported.

You call getSupportFragmentManager() because you are using the support library and the AppCompatActivity class. If you were not interested in using the support library, then you would subclass Activity and call getFragmentManager().

Fragment transactions

Now that you have the FragmentManager, add the following code to give it a fragment to manage. (We will step through this code afterward. Just get it in for now.)

Listing 7.16  Adding a CrimeFragment (CrimeActivity.java)

public class CrimeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_crime);

        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragment_container);

        if (fragment == null) {
            fragment = new CrimeFragment();
            fm.beginTransaction()
                .add(R.id.fragment_container, fragment)
                .commit();
        }
    }
}

The best place to start understanding the code you just added is not at the beginning. Instead, find the add(…) operation and the code around it. This code creates and commits a fragment transaction.

        if (fragment == null) {
            fragment = new CrimeFragment();
            fm.beginTransaction()
                .add(R.id.fragment_container, fragment)
                .commit();

Fragment transactions are used to add, remove, attach, detach, or replace fragments in the fragment list. They are the heart of how you use fragments to compose and recompose screens at runtime. The FragmentManager maintains a back stack of fragment transactions that you can navigate.

The FragmentManager.beginTransaction() method creates and returns an instance of FragmentTransaction. The FragmentTransaction class uses a fluent interface – methods that configure FragmentTransaction return a FragmentTransaction instead of void, which allows you to chain them together. So the code highlighted above says, Create a new fragment transaction, include one add operation in it, and then commit it.

The add(…) method is the meat of the transaction. It has two parameters: a container view ID and the newly created CrimeFragment. The container view ID should look familiar. It is the resource ID of the FrameLayout that you defined in activity_crime.xml.

A container view ID serves two purposes:

  • It tells the FragmentManager where in the activity’s view the fragment’s view should appear.

  • It is used as a unique identifier for a fragment in the FragmentManager’s list.

When you need to retrieve the CrimeFragment from the FragmentManager, you ask for it by container view ID:

        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragment_container);

        if (fragment == null) {
            fragment = new CrimeFragment();
            fm.beginTransaction()
                .add(R.id.fragment_container, fragment)
                .commit();
        }

It may seem odd that the FragmentManager identifies the CrimeFragment using the resource ID of a FrameLayout. But identifying a UI fragment by the resource ID of its container view is built into how the FragmentManager operates. If you are adding multiple fragments to an activity, you would typically create separate containers with separate IDs for each of those fragments.

Now we can summarize the code you added in Listing 7.16 from start to finish.

First, you ask the FragmentManager for the fragment with a container view ID of R.id.fragment_container. If this fragment is already in the list, the FragmentManager will return it.

Why would a fragment already be in the list? The call to CrimeActivity.onCreate(Bundle) could be in response to CrimeActivity being re-created after being destroyed on rotation or to reclaim memory. When an activity is destroyed, its FragmentManager saves out its list of fragments. When the activity is re-created, the new FragmentManager retrieves the list and re-creates the listed fragments to make everything as it was before.

On the other hand, if there is no fragment with the given container view ID, then fragment will be null. In this case, you create a new CrimeFragment and a new fragment transaction that adds the fragment to the list.

CrimeActivity is now hosting a CrimeFragment. Run CriminalIntent to prove it. You should see the view defined in fragment_crime.xml, as shown in Figure 7.19.

Figure 7.19  CrimeFragment’s view hosted by CrimeActivity

Screenshot shows CriminalIntent app in Android.

The FragmentManager and the fragment lifecycle

Now that you know about the FragmentManager, let’s take another look at the fragment lifecycle (Figure 7.20).

Figure 7.20  The fragment lifecycle, again

The fragment lifecycle, again

The FragmentManager of an activity is responsible for calling the lifecycle methods of the fragments in its list. The onAttach(Context), onCreate(Bundle), and onCreateView(…) methods are called when you add the fragment to the FragmentManager.

The onActivityCreated(Bundle) method is called after the hosting activity’s onCreate(Bundle) method has executed. You are adding the CrimeFragment in CrimeActivity.onCreate(Bundle), so this method will be called after the fragment has been added.

What happens if you add a fragment while the activity is already resumed? In that case, the FragmentManager immediately walks the fragment through whatever steps are necessary to get it caught up to the activity’s state. For example, as a fragment is added to an activity that is already resumed, that fragment gets calls to onAttach(Context), onCreate(Bundle), onCreateView(…), onActivityCreated(Bundle), onStart(), and then onResume().

Once the fragment’s state is caught up to the activity’s state, the hosting activity’s FragmentManager will call further lifecycle methods around the same time it receives the corresponding calls from the OS to keep the fragment’s state aligned with that of the activity.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
54.197.64.207