bluexmarker
bluexmarker

Reputation: 373

fragmentContainer is a unique identifier for a fragment?

I am confused by Fragment managers and fragments. In the book "Android Programming" by big nerd ranch, they instantiate a fragment in the MainActivity.

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

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

In the second line, they find the fragment not by the ID of the fragment, but by the ID of the fragmentContainer. FragmentContainer is just a plain layout. The book says that "[the container view ID] is used as a unique identifier for a fragment in the FragmentManager's list.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

On a different page, when coding this fragmentContainer, they write "You can and will use this same layout to host other fragments."

If this layout can host other fragments, how can this layout be used as a unique identifier for a fragment in the FragmentManager's list? Why don't they do

fm.findFragmentById(R.id.[some fragment id here]) 

and instead using the Id of a fragment container that that can host other fragments as well?

Upvotes: 1

Views: 2526

Answers (2)

tir38
tir38

Reputation: 10421

they find the fragment not by the ID of the fragment, but by the ID of the fragmentContainer.

The cool thing is that the Fragment ID and the Fragment Container ID are the same thing! Update your code to add some logging statements:

@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) {
        Log.d(TAG, "fragment id: " + fragment.getId());
    }

    View fragmentContainer = findViewById(R.id.fragment_container);
    Log.d(TAG, "fragment container id: " + fragmentContainer.getId());

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

Then run the app and rotate the device:

CrimeActivity: fragment container id: 2131296335
DEVICE ROTATION
CrimeActivity: fragment id: 2131296335
CrimeActivity: fragment container id: 2131296335

Obviously, your ID will be different than mine.

When you create the FragmentTransaction

fm.beginTransaction().add(R.id.fragmentContainer, fragment)

the FragmentManager will use the ID as both a way to identify this fragment from the list of fragments that it is managing and as the location in the view hierarchy to display the fragment.

On a different page, when coding this fragmentContainer, they write "You can and will use this same layout to host other fragments."

If this layout can host other fragments, how can this layout be used as a unique identifier for a fragment in the FragmentManager's list?

That last sentence should be clarified to read

"You can and will use this same layout to host other fragments, by replacing the current fragment with a different one."

There will only ever be one fragment with that ID and only one spot in the view hierarchy with that id. Jump ahead to code listing 17.8 (in the second edition) to see a replacement example.

if (findViewById(R.id.detail_fragment_container) == null) {
    Intent intent = CrimePagerActivity.newIntent(this, crime.getId());
    startActivity(intent);
} else {
    Fragment newDetail = CrimeFragment.newInstance(crime.getId());
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.detail_fragment_container, newDetail)
            .commit();
}

The replace call with throw way whatever Fragment was currently in detail_fragment_container and insert newDetail fragment, setting it's ID to R.id.detail_fragment_container.

Why not use Tags?

One comment suggested:

I would use tags: .add(R.id.fragmentContainer, fragment, "MY TAG") and then: findFragmentById("MY TAG")

This doesn't directly solve the problem. Now you have two ways to identify a Fragment: it's Tag and it's ID (a Fragment will still be given an ID just by the fact that you are adding it to R.id.fragment_container).

Given two ways to identify the Fragment, it will be up to you as a developer to keep track of what is displayed where. Which Fragment is this (what is it's TAG) and where is it being displayed (what is it's ID)?

Upvotes: 2

An SO User
An SO User

Reputation: 24998

Finds a fragment that was identified by the given id either when inflated from XML or as the container ID when added in a transaction. This first searches through fragments that are currently added to the manager's activity; if no such fragment is found, then all fragments currently on the back stack associated with this ID are searched.

That's the docs for findFragmentById( ). If the fragment was inflated from XML using the fragment tag, then it will do exactly as you said. If it was added dynamically by using a transaction, it'll look for fragments with the associated container ID.

Upvotes: 0

Related Questions