Android Tutorial: Fragments

In this tutorial I will provide an example how you can implement fragments. Fragments are often used to create the tabs in the action bar which can be clicked or swiped through. We will create a small app which explains some of the sights of the town Maastricht, Netherlands. Of course you can adjust the code for you own purposes. In this Android Tutorial we will create:

  • Install support package to enable fragments in the Eclipse environment.
  • Create the tabs through fragments
  • A layout of each of the tabs

After this tutorial you will have the code for the following app:

To support fragments you need to install the android.support.v4.app package (supports for example Fragments, FragmentActivity etc).

You can install this via the Eclipse environment, if already running on the Android SDK:

  1. Launch the Android SDK Manager.From Eclipse, you can select WindowAndroid SDK Manager. Or, launch SDK Manager.exe from the <sdk>/ directory (on Windows only) or android from the <sdk>/tools/ directory.
  2. Expand the Android Repository, check Android Support package and click Install selected.
  3. Proceed to install the package.

For more information on these support packages click here.

Now that we have support for Fragments in our SDK and Eclipse environment we will first set-up our layouts.

In my example each layout represent one of the sights in the Town Maastricht: Cityhall, Servaas Church, Helpoort and Minckeleers. Each sight has it’s own tab in the action bar and the layout is for each sight the same.

I applied the same naming convention for each layout: frag_namesight.xml

frag_cityhall.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title_cityhall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="Cityhall Maastricht"
        android:textColor="#0099CC"  
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv_cityhall"
        android:layout_width="wrap_content"
        android:layout_height="122dp"
        android:adjustViewBounds="true"
        android:baselineAlignBottom="false"
        android:padding="@dimen/padding5dp"
        android:scaleType="fitCenter"
        android:src="@drawable/stadshuis" />

    <ImageView
        android:id="@+id/ivcityhallmaps"
        android:layout_width="match_parent"
        android:layout_height="122dp"
        android:padding="@dimen/padding5dp"
        android:src="@drawable/maps" />
    
</LinearLayout>
    <TextView
        android:id="@+id/tvheaderhist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="History"
        android:textColor="#0099CC" 
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/tvcityhall"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="@string/cityhallcontent" />
</LinearLayout>
</ScrollView>

frag_helpoort.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title_helpoort"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="Helpoort Maastricht"
        android:textColor="#0099CC"  
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv_helpoort"
        android:layout_width="wrap_content"
        android:layout_height="122dp"
        android:adjustViewBounds="true"
        android:baselineAlignBottom="false"
        android:padding="@dimen/padding5dp"
        android:scaleType="fitCenter"
        android:src="@drawable/helpoort" />

    <ImageView
        android:id="@+id/ivcityhallmaps"
        android:layout_width="match_parent"
        android:layout_height="122dp"
        android:padding="@dimen/padding5dp"
        android:src="@drawable/maps" />
    
</LinearLayout>
    <TextView
        android:id="@+id/tvheaderhist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="History"
        android:textColor="#0099CC" 
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/tvhelpoort"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="@string/helpoortcontent" />
</LinearLayout>
</ScrollView>

frag_minkelers.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title_minckeleers"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="Minckeleers Maastricht"
        android:textColor="#0099CC"  
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv_minckeleers"
        android:layout_width="wrap_content"
        android:layout_height="122dp"
        android:adjustViewBounds="true"
        android:baselineAlignBottom="false"
        android:padding="@dimen/padding5dp"
        android:scaleType="fitCenter"
        android:src="@drawable/minckeleers" />

    <ImageView
        android:id="@+id/ivcityhallmaps"
        android:layout_width="match_parent"
        android:layout_height="122dp"
        android:padding="@dimen/padding5dp"
        android:src="@drawable/maps" />
    
</LinearLayout>
    <TextView
        android:id="@+id/tvheaderhist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="History"
        android:textColor="#0099CC" 
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/tvminckeleers"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="@string/Minckeleerscontent" />
</LinearLayout>
</ScrollView>

frag_servaas.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical">

    <TextView
        android:id="@+id/tv_title_servaas"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="St Servaas Maastricht"
        android:textColor="#0099CC"  
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

<LinearLayout 
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="horizontal">
    <ImageView
        android:id="@+id/iv_servaas"
        android:layout_width="wrap_content"
        android:layout_height="122dp"
        android:adjustViewBounds="true"
        android:baselineAlignBottom="false"
        android:padding="@dimen/padding5dp"
        android:scaleType="fitCenter"
        android:src="@drawable/servaas" />

    <ImageView
        android:id="@+id/ivcityhallmaps"
        android:layout_width="match_parent"
        android:layout_height="122dp"
        android:padding="@dimen/padding5dp"
        android:src="@drawable/maps" />
    
</LinearLayout>
    <TextView
        android:id="@+id/tvheaderhist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="History"
        android:textColor="#0099CC" 
        android:typeface="sans"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/tvservaas"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/padding5dp"
        android:text="@string/servaascontent" />
</LinearLayout>
</ScrollView>

Each layout consist out of:

  1. A scrollview to enable scroll in the fragment;
  2. TextView for the headers and titles;
  3. TextView for the content;
  4. Two image views, one for illustration and the second imageview which links to google maps.

The textview which contains the content refers to a string value. This string value holds the body content explaining more about the sights. I will not list them all in this blog past. But here you can see an example of the cityhall.xml. This content should be placed in your values folder of your Eclipse environment. NOTE: I made separate XML files with the property String, but of course you could place also all the content in the existing String XML in your project. This is a matter of preference.

Example cityhall.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="cityhallcontent">
        
The Town Hall of Maastricht is the town of Maastricht . From the 1662 revival-styled building, designed by Pieter Post is an important example of the Dutch Classicism . With Namur stone clad, detached building is located in the middle of the market and has a westward-looking monumental staircase.
nnThe contract to build the town hall in 1655 by the city council passed to Pieter Post. In 1659 came Mail from The Hague to Maastricht to the best place to find the new town hall to post. He chose the middle of the market, but in order to do the Cloth Hall , the Belfry , part of the city wall with the Lie Gate, the Prison and some houses on the market districts. The names of two adjoining streets "Small Canal" and "Grand Canal" still refer to the course of the first walls of Maastricht. By this demolition was a market square, where in the middle the new town would come. The stones were from the Prison are used in the foundations of the new city hall.
nnThe design of the town hall takes into account the two men, the Duke of Brabant and the prince-bishop of Liège to Maastricht until the end of the 17th century ruled. The left side of the building is the Brabant side, while the right side contains the Liège rooms. These are the main side because the prince-bishop higher in rank than the Duke.       
        

        </string>
</resources>

We are almost done, the last XML we need to add to our values folder is the Ids.xml. This is a reference resource Item which we will use in our MainActivity to set up the fragments. It’s called pager (referring to the android widget viewpager).

ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="id" name="pager"/>
</resources>

Now we have finished developing all XML files and we can start with the Java :). You  have to create in your src folder 4 new java classes which represent each on of your Fragments. (see screenshot).

Ok, you have created these Java classes make sure you let them extends Fragment (instead of activity).

public class FragCityhall extends Fragment {

I will only give the code of the FragCityhall.java, to keep this post a bit clean. But you can copy and past the code for all of your other fragments and only set the correct references to your layouts.

It’s fairly simple in each Fragment we inflate the layout we developed  and add a return statement.

@Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
	 
	 
	 
  View myFragmentView = inflater.inflate(R.layout.frag_cityhall, container, false);

 

Note that in this example we have also a reference to an ImageView. This is the Map image which launch the Google Maps application. I have included this to show you how you can reference to an Android widget without having to extend to Activity.

ImageView maps = (ImageView) myFragmentView.findViewById(R.id.ivcityhallmaps);

 
The complete Java code for FragCityhall.java is:

package com.christianpeeters.historymaatstricht;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

public class FragCityhall extends Fragment {
	

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
	 
	 
	 
  View myFragmentView = inflater.inflate(R.layout.frag_cityhall, container, false);
  
  ImageView maps = (ImageView) myFragmentView.findViewById(R.id.ivcityhallmaps);
  maps.setOnClickListener(new View.OnClickListener() {
	
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		Intent intent = new Intent(android.content.Intent.ACTION_VIEW, 
				Uri.parse("http://maps.google.com/?q=<50.851461>,<5.691043>"));
				startActivity(intent);
	}
}) ;
  
  
  return myFragmentView;

 }

}
	

Assuming you have set all your Fragments we now need to tie them together in the MainActivity.java.

In this MainActivity.java we do the following things:

  • We create a ViewPager and refer it to our Resource item;
  • We create an ActionBar;
  • We Create the Tabs via TabsAdapter;
  • We assign each fragment to the Tabs.
package com.christianpeeters.historymaatstricht;

import java.util.ArrayList;

import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.FragmentTransaction;
import android.content.Context;
import android.os.Bundle;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;


public class MainActivity extends FragmentActivity {
 
 ViewPager ViewPager;
 TabsAdapter TabsAdapter;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
      
        //create a new ViewPager and set to the pager we have created in Ids.xml
        ViewPager = new ViewPager(this);
        ViewPager.setId(R.id.pager);
        setContentView(ViewPager);
    
        //Create a new Action bar and set title to strings.xml
        final ActionBar bar = getActionBar();
        bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        bar.setTitle(R.string.title_activity_main);
       
        //Attach the Tabs to the fragment classes and set the tab title.
        TabsAdapter = new TabsAdapter(this, ViewPager);
        TabsAdapter.addTab(bar.newTab().setText("CityHall"),
                FragCityhall.class, null);
        TabsAdapter.addTab(bar.newTab().setText("Helpoort"),
          FragHelpoort.class, null);
        TabsAdapter.addTab(bar.newTab().setText("Minckeleers"),
          FragMinkelers.class, null);
        TabsAdapter.addTab(bar.newTab().setText("St Servaas"),
                FragServaas.class, null);
        
        if (savedInstanceState != null) {
            bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
        }

    }

 @Override
 protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
        outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
        
 }
 // create TabsAdapter to create tabs and behavior
 public static class TabsAdapter extends FragmentPagerAdapter
  implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
  
  private final Context mContext;
        private final ActionBar mActionBar;
        private final ViewPager mViewPager;
        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
        

        static final class TabInfo {
            private final Class<?> clss;
            private final Bundle args;

            TabInfo(Class<?> _class, Bundle _args) {
                clss = _class;
                args = _args;
            }
        }

  public TabsAdapter(FragmentActivity activity, ViewPager pager) {
   super(activity.getSupportFragmentManager());
            mContext = activity;
            mActionBar = activity.getActionBar();
            mViewPager = pager;
            mViewPager.setAdapter(this);
            mViewPager.setOnPageChangeListener(this);
        }

  public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) {
            TabInfo info = new TabInfo(clss, args);
            tab.setTag(info);
            tab.setTabListener(this);
            mTabs.add(info);
            mActionBar.addTab(tab);
            notifyDataSetChanged();
            
        }

  @Override
  public void onPageScrollStateChanged(int state) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onPageSelected(int position) {
   // TODO Auto-generated method stub
   mActionBar.setSelectedNavigationItem(position);
  }

  @Override
  public void onTabReselected(Tab tab, FragmentTransaction ft) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onTabSelected(Tab tab, FragmentTransaction ft) {
   Object tag = tab.getTag();
            for (int i=0; i<mTabs.size(); i++) {
                if (mTabs.get(i) == tag) {
                    mViewPager.setCurrentItem(i);
                }
            }
  }

  @Override
  public void onTabUnselected(Tab tab, FragmentTransaction ft) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public Fragment getItem(int position) {
   TabInfo info = mTabs.get(position);
            return Fragment.instantiate(mContext, info.clss.getName(), info.args);
  }

  @Override
  public int getCount() {
   return mTabs.size();
  }

 }
 

}

 

As you can see in the screenshots on top, I have the light/holo design instead of the black dark. You can do this easily in the following way.

Set in styles.xml:

<style name="LightThemeSelector" parent="android:Theme.Holo.Light"/>

And in your manifest make the reference:

android:theme="@style/LightThemeSelector"

 

21 Responses

  1. Anonymous September 29, 2012 / 3:38 pm

    That is a fantastic tutorial.

  2. ramil joaquin October 4, 2012 / 12:32 am

    cool !

  3. Mike November 28, 2012 / 4:01 am

    Great. Any chance you could have a link to the actual example code?

  4. Zach January 11, 2013 / 8:57 am

    Out of all the tutorials and examples for fragmented “Tab” style menu system this the only one that is not only extremely clear to read but is done the best/most efficient way.

    Fantastic tutorial, thanks!!

  5. Javier January 28, 2013 / 11:06 pm

    Hi thanks for this fantastic tutorial, work fine, but i don´t know how to do to see the cityhall.xml. in the app i have copied the file to values folder but i can´t see nothing i´m new on android please can you help me. thanks in advance

    • Christian January 29, 2013 / 12:01 am

      Hi Javier,

      In your frag_cityhall.xml (the layout of your tab page) has a textview which populates the text from your cityhall.xml

      android:text=”@string/cityhallcontent”/> refers to your string value in your cityhall.xml ()

      -part of your frag_cityhall.xml-:

      I hope this helps. Cheers Christian

  6. Javier January 29, 2013 / 10:00 am

    Hi Cristian thank you very much for your fast help, i just write androidtext: content…. without the string value, thanks a lot. I don´t have any background in android and its really difficult for me, but you are an inspiration i will follow you in here. Sorry because my english is not very good.

    Best regards Javier

  7. jurgen February 27, 2013 / 12:30 am

    Hi, i have reading out and i will definitely bookmarrk your site, just wanted to say i liked this article.

  8. Kristy March 20, 2013 / 7:33 pm

    Any chance you can publish the code on github or some place so I don’t have to edit out all the line numbers when cutting and pasting example code?

  9. Kristy March 20, 2013 / 11:46 pm

    Another question I have is where is the main layout for this project? Does it hold a fragment?

  10. Vijayaramanp March 23, 2013 / 8:46 pm

    Thanks you so much!. Your tutorial made me more interested in android.
    Thanks a billion.
    Is it possible to have single fragment for the above example?. If yes, kindly share that type of example with my mail id.

    Regards,
    Vijay

  11. Sanjay May 10, 2013 / 5:59 am

    Thanks for this tutorial. this is very useful.
    Can you please help me a little more on this tutorial?
    I followed your instructions and completed program for this tutorial and its working fine when I am writing code on main view. But if Im adding a single button on main view which loading another view (child view) and I m writing this code on child view then application getting crashed.

    Can you please guide me how to do this for a child view of main view?

    Thanks in advance..

  12. wondesen May 31, 2013 / 3:19 pm

    It is really very nice tutorial. Thank u very much!!!

  13. mahesh lakher July 17, 2013 / 3:50 pm

    Hi it’s working good.
    i want to implement search dialog which give suggestion list (may be Company List) and when i click on any suggestion(Company) it provide a Detail page related to company.
    i have completed almost but when i click on suggestion(company). it gives exception and crash.
    Exception:
    “java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.horse.fragmentdemo2/com.horse.fragmentdemo2.FragCityhall}: java.lang.ClassCastException: com.horse.fragmentdemo2.FragCityhall cannot be cast to android.app.Activity”

  14. arjun July 17, 2013 / 9:29 pm

    nice tutorial for fragments
    thanks

  15. karthik July 24, 2013 / 1:22 pm

    Nice tutorial..,
    How to remove action bar in this tutorial …
    Thank You

  16. Sonali September 18, 2013 / 1:01 pm

    Awesome tutorial, where is the link to download complete source code ?

  17. Andy Res October 4, 2013 / 9:37 am

    Nice example, thanks for the tutorial.

  18. lestersoriano November 19, 2013 / 7:25 pm

    what if i want to reload a tab that already created

    TabsAdapter.addTab(bar.newTab().setText(“St Servaas”),
    FragServaas.class, null);

    What if I have the parramaters for FlagServaas has change ..

    how can i replace the fragment of a tab that already exist?

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*