About Francesco Azzola

I'm a senior software engineer with more than 15 yrs old experience in JEE architecture. I'm SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. I'm an android enthusiast and i've worked for long time in the mobile development field.

Android Fragment transaction: FragmentManager and 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_tutorial2android_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

Related Whitepaper:

Rapid Android Development: Build Rich, Sensor-Based Applications with Processing

Create mobile apps for Android phones and tablets faster and more easily than you ever imagined

Use 'Processing', the free, award-winning, graphics-savvy language and development environment, to work with the touchscreens, hardware sensors, cameras, network transceivers, and other devices and software in the latest Android phones and tablets.

Get it Now!  

One Response to "Android Fragment transaction: FragmentManager and Backstack"

  1. BalaVishnu says:

    very nice..thx

Leave a Reply


nine − 8 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books