Android development blog
Tutorials about Android dev topics

Android Fragment transaction: FragmentManager and Backstack

Topics covered

Android Fragment

Fragment manager (FragmentManager): add, replace, remove fragment

Fragment backstack

Fragments are useful when we want to support multiple screen size. To manage fragments we need a
FragmentManager that help us to handle trasaction between fragments.  With transaction we mean a sequence of steps to add, replace or remove fragments. In the last post we showed how to support multiple screen size and orientation using fragments. Now we want to go a bit further.
In Android there are two different methods to create fragment:
  • static method
  • dynamic method
Static method is when we “write” directly our fragment in XML file. This is an example:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:orientation="horizontal" >

        <fragment android:id="@+id/listFragment"
              android:layout_width="0dp"
              android:layout_height="wrap_content"
              class="com.survivingwithandroid.fragment.LinkListFragment"
              android:layout_weight="2"/>
</LinearLayout>

In this case our layout isn’t dynamic because we can’t manage the fragment at runtime. So if we want to make our layout dynamic (and this happen very often) we need to do it in another way. We have to use FrameLayout. With FrameLayout we can handle fragments as we need at runtime, but to do it we need a manager, in other words a component that can handle fragments. This is FragmentManager. This component can add, replace and remove fragments at runtime. The layout above becomes:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

   
    <FrameLayout
        android:id="@+id/listFragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
    />
   
</RelativeLayout>

So now we have all the freedom to “inject” in the FrameLayout our fragments.



FragmentManager


As we said before FragmentManager is the key component. Using FragmentManager we can discover (find) fragment inside our layout using findFragmentById or findFragmentByTag. While the first method is very simple and we use the common android id to discover the component, the second method (that uses tag) is unsual. A tag in a fragment is simply a “name” we give to the fragment so that we can find it later using that name. We are more interested in some other methods.

All the operation that are made by the FragmentManager happens inside a “transaction” like in a database operation. First, we can get the FragmentManger using the Activity method getFragmentManager(). Once we have the reference to this component we have to start the transaction in this way:
FragmentManager fm = getFragmentManager();

// Transaction start
FragmentTransaction ft = fm.beginTransaction();

.......

// Transaction commint
ft.commit();

At the end when we finished and we are ready to show our fragment we have to call the commit method that marks the end of the transaction. For example, if we remember the example we showed in the last post, and using a FrameLayout we can “insert” the link list in this way:
public class MainActivity extends Activity implements ChangeLinkListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        
        LinkListFragment llf = new LinkListFragment();
        ft.replace(R.id.listFragment, llf);
        ft.commit();
    }
  ...


}

But…What kind of operation we can perform inside the transaction?…Well we can:
  • add a new fragment
  • replace an existing fragment
  • remove a fragment
If we remember the example we used last time every time an user clicks on a link the interface method onLinkChange is called. So in this method we want to show how to perform the operation listed above.
if (findViewById(R.id.fragPage) != null) {
    WebViewFragment wvf = (WebViewFragment) getFragmentManager().findFragmentById(R.id.fragPage);
   
// Part 1: Tablet and so on
    if (wvf == null) {
        System.out.println("Dual fragment - 1");
        wvf = new WebViewFragment();
        wvf.init(linkData.getLink());
        // We are in dual fragment (Tablet and so on)
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        //wvf.updateUrl(link);
        ft.add(R.id.fragPage, wvf);
        ft.commit();
        
    }
    else {
     Log.d("SwA", "Dual Fragment update");
     wvf = new WebViewFragment();
      wvf.init(linkData.getLink());
      FragmentManager fm = getFragmentManager();
     FragmentTransaction ft = fm.beginTransaction();            
     ft.replace(R.id.fragPage, wvf);
     ft.commit();
     
     //wvf.updateUrl(linkData.getLink());
    }
}
else {
    Log.d("SwA", "replace");
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    WebViewFragment wvf =  new WebViewFragment();
    wvf.init(linkData.getLink());
    ft.replace(R.id.listFragment, wvf);
    ft.commit();
}

If you compare this piece of code with the last example in the previous post you notice some differences. First in the green part we don’t start an activity anymore when user clicks on a link but we simply replace the FrameLayout with a fragment showing the web page. More over, in the yellow part we don’t update the current fragment inside the FrameLayout but we create a new fragment and we replace the existing fragment with the one just created. The app behaviour is always the same but we obtained this behaviour in a different way, using dynamic fragments inside our layout.

If you run the app and start using it you can notice a “wired” behaviour when you press back button. We’d expect that the back button would bring us to the last web page visited but it isn’t like we supposed. When you press back button you come to the home page.

android_fragment_tutorial1android_fragment_tutorial2android_fragment_tutorial3

Why?

Fragment Backstack


Well the behaviour described above is normal because the back button acts at activity level not at the fragment level. So our activity is the same while we replace fragments as the user interacts with the app. In this way when we tap on the back button we select the first activity on the activity stack, in our case the home. We don’t want this behaviour but we want that when we click on the back button we go back in the fragments stack. We can achieve it adding the fragment to the backstack. We do it in this way:
@Override
public void onLinkChange(LinkData linkData) {        
    System.out.println("Listener");
    // Here we detect if there's dual fragment
    if (findViewById(R.id.fragPage) != null) {
        WebViewFragment wvf = (WebViewFragment) getFragmentManager().findFragmentById(R.id.fragPage);
       
        if (wvf == null) {
            System.out.println("Dual fragment - 1");
            wvf = new WebViewFragment();
            wvf.init(linkData.getLink());
            // We are in dual fragment (Tablet and so on)
            FragmentManager fm = getFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            //wvf.updateUrl(link);
            ft.add(R.id.fragPage, wvf);
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            // Add to backstack
            ft.addToBackStack(linkData.getName());
            ft.commit();
            
        }
        else {
         Log.d("SwA", "Dual Fragment update");
         wvf = new WebViewFragment();
          wvf.init(linkData.getLink());
          FragmentManager fm = getFragmentManager();
         FragmentTransaction ft = fm.beginTransaction();            
         ft.replace(R.id.fragPage, wvf);
         ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
          // Add to backstack
          ft.addToBackStack(linkData.getName());
          ft.commit();
         
         //wvf.updateUrl(linkData.getLink());
        }
    }
    else {
        /*
        System.out.println("Start Activity");
        Intent i = new Intent(this, WebViewActivity.class);
        i.putExtra("link", link);
        startActivity(i);
        */
           Log.d("SwA", "replace");
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        WebViewFragment wvf =  new WebViewFragment();
        wvf.init(linkData.getLink());
        
        ft.addToBackStack(linkData.getName());
        ft.replace(R.id.listFragment, wvf);
        ft.commit();
    }
    
}

We use the addToBackStack method of the FragmentTrasaction and we add every fragment to the backstack. In this way when we tap on the back button we have the correct behaviour.

Source code available soon

9 comments:

  1. This series about fragments is the best I found.
    Thanks for sharing.

    It not only shows knowledge about the matter but, what is more need in this job, expertise to explain it.


    JJDS

    ReplyDelete
  2. survivingwithandroidSeptember 4, 2013 at 2:46 PM

    Thx. I'm glad you liked my work

    ReplyDelete
  3. Good Example for fragment

    ReplyDelete
  4. What happens to the back stack on rotate? Is it set to null?

    ReplyDelete
  5. did you find out? I am currently struggling with this.

    ReplyDelete
  6. Can you share source code link ? Thanks

    ReplyDelete
  7. I am also having problem to understand this...Do u have any ideas?

    ReplyDelete
  8. I'm going to lool at this in depth soon. I will let you know what I find or come up with

    ReplyDelete
  9. In json parsing, how authenticate digital signature in android, Please help me

    ReplyDelete

Related Posts with Thumbnails