Search code examples
androidandroid-fragmentsmaster-detailfragmenttransaction

Android fragment replace doesn't do anything


I'm hoping someone can clear up what I am doing wrong here. I have a common Master Detail on a tablet in landscape mode paradigm. The only caveat is that some elements in the master open FragA in the Detail section, and others open FragB in the detail section. Everything seems fine, except the actual process of switching the fragments out. Whichever fragment gets shown first is the only one which shows. I have been using the replace method to swap them out, and all the SO comments on the subject I can find point out that this will not work if I have added the detail fragments in XML. Unfortunately, this does not apply for me since I add all fragments dynamically. My code looks like this:

FragmentInfo frag = (Helper.isImageViewable(fileSelected)) ?  getFragInfoByClass(ImageViewerFragment.class) : getFragInfoByClass(DownloadProgressFragment.class) ;
         FragmentTransaction ft = getFragmentManager().beginTransaction();
         if(frag.mFragment == null){
             frag.mFragment = Fragment.instantiate(NewMainActivity.this, frag.mClass.getName());
             ft.add(frag.target.getId(), frag.mFragment);
             ft.commit();
             ft = getFragmentManager().beginTransaction();
         } 
         ft.replace(frag.target.getId(), frag.mFragment);
         ft.commit();

This is the only code used anywhere for adding these fragments, and what seems to happen is whichever fragment is added first works fine, but the replace method does not work at all. Whichever fragment was there is just... still there.

Does anyone know what I am doing wrong?

Edit

The relevant section of the layout file looks like this:

    <LinearLayout
    android:id="@+id/fragments_holder"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <FrameLayout
        android:id="@+id/list_fragment"
        android:layout_width="0dp"
        android:layout_weight="0.39"
        android:layout_height="match_parent" />

    <FrameLayout
        android:id="@+id/details_fragment"
        android:layout_height="match_parent"
        android:layout_weight="0.61"
        android:layout_width="0dp" />

    <!-- ADD METADATA VIEW HERE -->

</LinearLayout>

Both FragmentInfo objects correctly have a target of the FrameLayout with id/details_fragment as demonstrated by the fact that either one works properly if it is the first one added.

Edit 2 Note: using this block of code also has the exact same result:

FragmentInfo frag = (Helper.isImageViewable(fileSelected)) ?  getFragInfoByClass(ImageViewerFragment.class) : getFragInfoByClass(DownloadProgressFragment.class) ;
         FragmentTransaction ft = getFragmentManager().beginTransaction();
         if(frag.mFragment == null){
             frag.mFragment = Fragment.instantiate(NewMainActivity.this, frag.mClass.getName());
             ft.add(frag.target.getId(), frag.mFragment);
             //ft.commit();
             //ft = getFragmentManager().beginTransaction();
         } else{
             ft.replace(frag.target.getId(), frag.mFragment);
         }
         //ft.replace(frag.target.getId(), frag.mFragment);
         ft.commit();
         detailsContainer.setFragmentInfo(frag);

Solution

  • Replace is effectively the same as a series of remove and add operations. Even if the container is empty, replace is still safe to use (it is then the same as a simple add). I prefer using explicit remove and add operations since it is easier to reason about them.

    I think your code samples have some problems in the beginning, but there seems to be some correct paths through it once initialization is complete. In the first code sample, you are calling add and replace with the same fragment instance (in the case where the fragment has not yet been created). In your second code sample, you do not remove the old fragment (in the case where the second fragment to be added has not yet been created).

    I think your first code sample will perform replace operations correctly if you comment out (remove) the double-adding:

    FragmentInfo frag = (Helper.isImageViewable(fileSelected)) ?  getFragInfoByClass(ImageViewerFragment.class) : getFragInfoByClass(DownloadProgressFragment.class) ;
             FragmentTransaction ft = getFragmentManager().beginTransaction();
             if(frag.mFragment == null){
                 frag.mFragment = Fragment.instantiate(NewMainActivity.this, frag.mClass.getName());
                 // ft.add(frag.target.getId(), frag.mFragment);
                 // ft.commit();
                 // ft = getFragmentManager().beginTransaction();
             } 
             ft.replace(frag.target.getId(), frag.mFragment);
             ft.commit();