nKn
nKn

Reputation: 13761

Why doesn't detach() work on FragmentTransaction and remove() does?

I'm working on a TabHost whose tabs have associated a Fragment (each tab a different one). Each of those Fragments have inside an instance of another Fragment which is a login bar that has two states: Logged in or not.

Logged out example

Logged out

Logged in example

Logged in

In layout terms, each of these states have associated a View (a TextView for the non-logged-in case and a LinearLayout for the logged-in case), so if one of them is VISIBLE, the other one is GONE. As per the tab content, this is an example of the code of one of them (firsttab.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:ads="http://schemas.android.com/apk/lib/com.google.android.gms.ads"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#0000FF"
  android:orientation="vertical">

  <!-- That's the login bar -->
  <fragment 
    android:id="@+id/firsttab_loginrow"
    class="com.mydomain.myproject.LoginRowFragment"
    android:tag="1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

  <LinearLayout
    android:id="@+id/firsttab_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="10dp"
    android:gravity="center"
    android:orientation="horizontal">
  </LinearLayout>
</LinearLayout>

The inner Fragment (com.mydomain.myproject.LoginRowFragment) is defined this way:

<!-- If the user is not logged in -->
<TextView
   android:id="@+id/identification_nologin"
   android:layout_width="wrap_content"
   android:layout_height="match_parent"
   android:textColor="#FFFFFF" />

<!-- if the user is logged in -->
<LinearLayout
   android:id="@+id/identification_didlogin"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:orientation="horizontal" >

   ...      
</LinearLayout>

A general schema would be something like this:

Schema

The problem comes when I'm handling the tab change event by either attach()ing or detach()ing the correspondent parent Fragment (in this case, firsttab.xml). Previously to attaching/detaching the parent, I try detach()ing the login Fragment (the inner one), but it doesn't fire the onDetach() callback. The same happens when attach()ing. The weird thing is that if I replace .detach() with .remove(), it works just fine.

@Override
public void onTabChanged(final String tag) {
  final TabInfo newTab = mTabInfo.get(tag);

  if (lastTab != newTab) {

    final FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
    if ((lastTab != null) && (lastTab.getFragment() != null)) {

      // This is where it fails detaching. tabInfo is just a structure where I store some data
      // about the tabs to handle them. If I use remove() instead of the next detach() on loginFrag,
      // the onAttach()/onDetach() callbacks are not called. I'm quite sure everything is ok, loginFrag
      // is not null, it's actually the Fragment that should be detached, etc.

      final Fragment loginFrag = (Fragment) lastTab.getFragment().getActivity().getSupportFragmentManager().findFragmentById(lastTab.getLoginFragId());
      ft.detach(loginFrag);

      ft.detach(lastTab.getFragment());
    }

    if (newTab != null) {
      if (newTab.getFragment() == null) {
        final TabFragmentInflater tabInf = new TabFragmentInflater();
        newTab.setFragment(Fragment.instantiate(this, tabInf.getClass().getName(), newTab.getArgs()));
        ft.add(R.id.realtabcontent, newTab.getFragment(), newTab.getTag());
      }
      else
        ft.attach(newTab.getFragment());
    }

    ft.commit();
    this.getSupportFragmentManager().executePendingTransactions();

    lastTab = newTab;
  }
}

So the question is the following:

Why onAttach()/onDetach() callbacks are not fired in the LoginRowFragment class when using .attach() or .detach() on them, but get fired if I use .add() or .remove() respectively?

Upvotes: 2

Views: 3485

Answers (1)

Martin Marconcini
Martin Marconcini

Reputation: 27236

You cannot remove an instance of a Fragment which has been declared in your layout XML file (by design)

When you use FragmentTransactions, you're manipulating ViewGroups containing the layout of a Fragment.

When you declare your Fragment/Class in a layout, it's part of the View hierarchy, so you can't remove it.

You can, however, add a New Fragment into that layout, because it will act as a container.

Try changing your Layout from:

    <fragment 
      android:id="@+id/firsttab_loginrow"
      class="com.mydomain.myproject.LoginRowFragment"
      android:tag="1"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />

to:

    <FrameLayout 
      android:id="@+id/firsttab_loginrow"
      android:tag="1"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />

And create a FragmentTransaction to add/replace/show/hide according to what you want to do.

Upvotes: 4

Related Questions