Android fragment example

What is an Android Fragment?

Well as the name suggests it just a piece of something and in case of android its a piece of an Activity. Just like an activity the fragment also has a very similar life cycle onCreate(), onStart(), onStop(), onDestroy(), etc. In addition to that it has onCreateView() and onActivityCreated(). The system calls the onCreateView() when it's time for the fragment to draw its user interface for the first time. In this method you must inflate your fragment view return it. You can return null if the fragment does not provide a UI. The onActivityCreated() method is called when the fragment's activity has been created.

Why would we use a fragment?

You can create all your logic in an Activity and then manipulate the screen based on device size and orientation but the amount of code to programmatically draw views and implement all the logic based on layout and/or orientation will be make the whole project more complex than it needs to be. This is where fragments come to rescue, you can separate out the logic based on a functional entity rather than how the whole application looks to the end user. The concept is similar to the Portal technology where you create more than one portlet and put them on a given page.

The example

we are going to implement how to display a list view and a detail view displayed side by side when the user is in a landscape mode and in the case of portrait mode we just display the list and then when the user clicks on the given entry we display the detail view. The list entries in this tutorial are web URLs and actual web page is the detail view. The objective is to create the two fragments that are totaly independent and don't talk to each other which was done with the help of implementing an interface on the list activity. The activity is notified when the user select an URL and it alerts the detail fragment to updates its view. In addition to that we dynamically add and remove fragments from the activity based on the layout and the user action. So in the case of the portrait mode we only inflate the list view and then swap it out when the user clicks on the URL link whereas in the landscape mode we inflate both views the frame layouts. This example doesn't use another activity to display the detail page as some other tutorials have used, the objective is to have a simple and easy to understand tutorial that covers all the dynamics of the power of Android Fragments and how to use them effectively without complicating the matters.

Fragments in Portrait Mode

android fragment portrait mode listview
android fragment portrait mode detail view

Fragments in Landscape Mode

android fragment landscape mode

Android Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.as400samplecode"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" 
        android:theme="@android:style/Theme.Holo.Light">>
        <activity
            android:name=".AndroidFragmentActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

String values - strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Fragment Example</string>
    <string name="detail_heading">Web Page Detail</string>
    <string name="list_heading">Web Page Listing</string>

</resources>

Layout for Portrait mode - main.xml

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


    <FrameLayout
        android:id="@+id/displayList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

    </FrameLayout>
  
</LinearLayout>

Layout for Landscape mode - main.xml

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



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

    </FrameLayout>
   

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

    </FrameLayout>

</LinearLayout>

Listview XML resource - list_view.xml

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



    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:text="@string/list_heading"
        android:textSize="20sp" />
    
    <ListView android:id="@+id/listofURLs" android:layout_width="match_parent"
  android:layout_height="wrap_content" />
    

</LinearLayout>

Listview Row Layout - url_list.xml

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp"
    android:textSize="16sp" >
</TextView>

Detail Web view XML resource - detail_view.xml

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

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/detail_heading" android:padding="5dp" 
        android:textSize="20sp" />

    <WebView
        android:id="@+id/pageInfo"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Android fragment for the Webview - DetailFragment.java

package com.as400samplecode;

import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class DetailFragment extends Fragment {

 String mURL = "";

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.v("DetailFragment", "onCreate()");
 }

 @Override
 public void onActivityCreated(Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);
  Log.v("DetailFragment", "onActivityCreated()");
  if (savedInstanceState != null) {
   mURL = savedInstanceState.getString("currentURL", "");
  }
  if(!mURL.trim().equalsIgnoreCase("")){
   WebView myWebView = (WebView) getView().findViewById(R.id.pageInfo);
   myWebView.getSettings().setJavaScriptEnabled(true);
   myWebView.setWebViewClient(new MyWebViewClient());
   myWebView.loadUrl(mURL.trim());
  }
 }

 @Override
 public void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putString("currentURL", mURL);
 }

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  Log.v("DetailFragment", "onCreateView()");
  View view = inflater.inflate(R.layout.detail_view, container, false);
  return view;
 }

 public void setURLContent(String URL) {
  mURL = URL;
 }

 public void updateURLContent(String URL) {
  mURL = URL;
  WebView myWebView = (WebView) getView().findViewById(R.id.pageInfo);
  myWebView.getSettings().setJavaScriptEnabled(true);
  myWebView.setWebViewClient(new MyWebViewClient());
  myWebView.loadUrl(mURL.trim());
 }
 
 private class MyWebViewClient extends WebViewClient {
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
             return false;
     }
 }
}

Android fragment for the Listview - ListFragment.java

package com.as400samplecode;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;

public class ListFragment extends Fragment {

 OnURLSelectedListener mListener;
    
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.v("ListFragment", "onCreate()");
 }

 @Override
 public void onActivityCreated(Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);
  Log.v("ListFragment", "onActivityCreated().");
  Log.v("ListsavedInstanceState", savedInstanceState == null ? "true" : "false");
  
  //Generate list View from ArrayList
  displayListView();
  
 }

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  Log.v("ListFragment", "onCreateView()");
  Log.v("ListContainer", container == null ? "true" : "false");
  Log.v("ListsavedInstanceState", savedInstanceState == null ? "true" : "false");
  if (container == null) {
            return null;
        }
  View view = inflater.inflate(R.layout.list_view, container, false);
  return view;
 }
 
 
 // Container Activity must implement this interface
    public interface OnURLSelectedListener {
        public void onURLSelected(String URL);
    }
    
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnURLSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnURLSelectedListener");
        }
    }
 
 private void displayListView() {

  //Array list of countries
  List<String> urlList = new ArrayList<String>();
  urlList.add("http://www.google.com");
  urlList.add("http://mail.google.com");
  urlList.add("http://maps.google.com");
  
  //create an ArrayAdaptar from the String Array
  ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(getActivity(),
    R.layout.url_list, urlList);
  ListView listView = (ListView) getView().findViewById(R.id.listofURLs);
  // Assign adapter to ListView
  listView.setAdapter(dataAdapter);
  
  //enables filtering for the contents of the given ListView
  listView.setTextFilterEnabled(true);

  listView.setOnItemClickListener(new OnItemClickListener() {
   public void onItemClick(AdapterView<?> parent, View view,
     int position, long id) {
    // Send the URL to the host activity
          mListener.onURLSelected(((TextView) view).getText().toString());
    
   }
  });
  
 }
 
}

Android activity for the application - AndroidFragmentActivity.java

package com.as400samplecode;

import com.as400samplecode.ListFragment.OnURLSelectedListener;

import android.app.Activity;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;

public class AndroidFragmentActivity extends Activity
implements OnURLSelectedListener {

 boolean detailPage = false;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Log.v("AndroidFragmentActivity", "onCreate()");
  Log.v("AndroidFragmentsavedInstanceState", savedInstanceState == null ? "true" : "false");

  setContentView(R.layout.main);

  if(savedInstanceState == null) {
   FragmentTransaction ft = getFragmentManager().beginTransaction();
   ListFragment listFragment = new ListFragment();
   ft.add(R.id.displayList, listFragment, "List_Fragment");
   ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
   ft.commit();
  }

  if(findViewById(R.id.displayDetail) != null){
   detailPage = true;
   getFragmentManager().popBackStack();

   DetailFragment detailFragment = (DetailFragment) getFragmentManager().findFragmentById(R.id.displayDetail);
   if(detailFragment == null){
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    detailFragment = new DetailFragment();
    ft.replace(R.id.displayDetail, detailFragment, "Detail_Fragment1");
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.commit();
   }
  }

 }

 
 @Override
 public void onURLSelected(String URL) {
  Log.v("AndroidFragmentActivity",URL);

  if(detailPage){
   DetailFragment detailFragment = (DetailFragment)
   getFragmentManager().findFragmentById(R.id.displayDetail);
   detailFragment.updateURLContent(URL);
  }
  else{
   DetailFragment detailFragment = new DetailFragment();
   detailFragment.setURLContent(URL);
   FragmentTransaction ft = getFragmentManager().beginTransaction();
   ft.replace(R.id.displayList, detailFragment, "Detail_Fragment2");
   ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
   ft.addToBackStack(null);
   ft.commit();
  }
 }

}

You can read more about fragments here - http://developer.android.com/guide/components/fragments.html


17 comments :

  1. I'm new to Android,
    These are code for the different files,
    could you also share the file tree?
    Thanks! these sample codes are very helpful for beginners

    ReplyDelete
  2. I don't believ this works does it? Doesn't the ListFragment require that your listview be specified with an id as from the documentation:

    "To do this, your view hierarchy must contain a ListView object with the id "@android:id/list" (or list if it's in code)"

    ReplyDelete
  3. hey

    i got an error when i implement OnURLSelectedListener, the error shows that define interface OnURLSelectedListener,change to onItemSelectedListener and somethingmore.i got error on most URL related statement.don't know what to do?

    with regards
    ajay

    ReplyDelete
  4. Love the tutorial, any chance you could provide the project zipped?

    ReplyDelete
  5. Thank you for this Android fragment example. I found it very helpful! I compiled a list of some top resources for using fragments to build user interfaces in Android applications. I included your post. Check it out/ feel free to share. http://www.verious.com/board/Giancarlo-Leonio/android-sdk-using-fragments Hope other developers find this useful too. :)

    ReplyDelete
  6. A quick check showed two main.xml files, and only one has a home in layout.main.xml.
    How can this possibly run like this?

    ReplyDelete
    Replies
    1. Put the other one in /res/layout-land

      Delete
    2. how i can write it ?
      setContentView(R.layout.activity_main); ??

      Delete
    3. when device is in landscape ,it will take from layout-land folder.no need to specify it.

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. what u have written

      Delete
  8. awesome tutorial plz provide the project zipped my mail id is:mangalpsingh@yahoo.com

    ReplyDelete
    Replies
    1. Hey, the above code is working for anyone..

      Delete
  9. It works fine for a Tablet ,but not a Handset. Reason being that the code does not support fragment switching on a single mode. Since, we create two frames in Tab, there is a connectivity with the xml files as per the code. The code will work fine for a handset,provided there's a switching mechanism of the frames from list view to detailed view. For more details refer : http://www.vogella.com/articles/AndroidFragments/article.html and
    http://www.techrepublic.com/blog/app-builder/get-started-with-android-fragments/1050

    ReplyDelete
  10. My name is Mark.
    I am new to android.
    I need help: In which sub-directories must save the codes.
    I thank you

    ReplyDelete
  11. Nice explanation... and is there project zip file.?

    ReplyDelete