Jump to content

How to use the Fragment class in the Android Honeycomb SDK preview

+ 1
  zigurd's Photo
Posted Feb 03 2011 04:18 PM

If you have written some Android code, or even read about Android programming, you know that some of the most important classes in Android are Activity, View, and the layout and widget subclasses of View.

Typically, an Android user interface is composed of views within layouts, such as a ListView within a LinearLayout. A hierarchy of View objects gets loaded or created when an Activity is started. On small screens, this is fine: Users move from screen to screen to access different parts of a program's UI, and the Activity class, and Android's concept of a task, support a back stack, which enables quick and intuitive forward and back traversal of a hierarchical user interface.

But when you spread a UI out over the surface of a tablet's screen, it calls for a different style of interaction. Some parts of the screen should remain constant over longer durations than others. A card-stack metaphor doesn't cut it.

While developers can implement interactions where parts of the screen change by showing and hiding views, Android's developers decided they needed more than just convention to encourage better large screen UIs and consistent implementation of the feel of the UI. Now, in order to facilitate this new kind of interaction, as part of the Android 3.0 SDK, we get a class called Fragment.

A Fragment object is something between a View and and Activity: It can be part of a layout, but it isn't a subclass of View. It implements the ComponentCallbacks interface, and it has a lifecycle, but that lifecycle is dependent on the Activity the Fragment object belongs to. Let's see what it can do for us in a tablet-sized user interface.

Here are the contents of the main.xml file that describes a screen layout for our application. In a list on the left of the screen some numbers will appear. Selecting a number from that list will create that many Android icons, displayed in a Fragment instance on the right.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/frags">

    <ListView
            android:id="@+id/number_list"
            android:layout_width="250dip"
            android:layout_height="match_parent" />

    <fragment class="com.example.fragmenttest.TestFragment"
            android:id="@+id/the_frag"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</LinearLayout>


The code for the Fragment subclass referred to in the layout in main.xml is below. In short, this Fragment subclass displays a number of Android icons. It remembers how many, in case, in the course of the Activity lifecycle, it needs to remember and restore its state. In case it is instantiated using the no-arguments constructor, without a number of Androids to display, it displays none. So the Fragment created when main.xml is inflated displays no Android icons.

package com.example.fragmenttest;

import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class TestFragment extends Fragment {
    private int nAndroids;
    
    public TestFragment() {
    	
    }

   /**
    * Constructor for being created explicitly
    */
   public TestFragment(int nAndroids) {
	   	this.nAndroids = nAndroids;
    }

    /**
     * If we are being created with saved state, restore our state
     */
    @Override
    public void onCreate(Bundle saved) {
        super.onCreate(saved);
        if (null != saved) {
        	nAndroids = saved.getInt("nAndroids");
        }
    }
    
    /**
     * Save the number of Androids to be displayed
     */
    @Override
    public void onSaveInstanceState(Bundle toSave) {
    	toSave.putInt("nAndroids", nAndroids);
    }

    /**
     * Make a grid and fill it with n Androids
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) {
    	int n;
    	Context c = getActivity().getApplicationContext();
        LinearLayout l = new LinearLayout(c);
        for (n = 0; n < nAndroids; n++) {
        	ImageView i = new ImageView(c);
        	i.setImageResource(R.drawable.android);
        	l.addView(i);
        }
        return l;
    }
}


This is what you see when you when you run the program: Initially, you see a list of numbers, and a blank area to the right.

Attached Image

Touch a number, and a new Fragment is created, with the specified number of Android icons. If you keep touching numbers in the list on the left, the corresponding number of Android icons appears in the Fragment instance on the right. Now try the back button. Instead of returning to an instance of the Activity that was on the screen before this application launched, you see the previous Fragment instance on the right. That is, the Fragment objects have been integrated into the task's back stack.

Attached Image

The Fragment instances are created by code in this app's Activity class. We'll take a look at that now to see how Fragment objects are integrated with the back stack by the Activity.

package com.example.fragmenttest;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class FragmentTestActivity extends Activity implements OnItemClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ListView l = (ListView) findViewById(R.id.number_list);
        ArrayAdapter numbers = new ArrayAdapter<String>(getApplicationContext(),
        		android.R.layout.simple_list_item_1, 
        		new String [] {
        	"one", "two", "three", "four", "five", "six"
        });
        l.setAdapter(numbers);
        l.setOnItemClickListener(this);
    }

    
    /**
     * Add a Fragment to our stack with n Androids in it
     */
    private void stackAFragment(int nAndroids) {
    	Fragment f = new TestFragment(nAndroids);
    	
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.the_frag, f);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        ft.addToBackStack(null);
        ft.commit();
    }

	/**
	 * Called when a number gets clicked
	 */
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		stackAFragment(position + 1);
	}
}


The code for creating and handling a click on a list item should be familiar to Android coders. But there us something new here: In the stackAFragment method, a Fragment instance is created, and then something interesting happens: A FragmentTransaction object is created, initialized, and, with the commit() method, causes a switch between Fragment instances on the screen.

The FragmentTransaction object handles transactions on Fragment instances. In this case, it handles replacing one Fragment instance on the screen with another and integrating the replaced instance with the Activity's back stack.

This is how an Activity object in an app designed for tablets can spread out over a larger screen while maintaining the forward/backward navigation that previously only happened from one Activity to another.

Fragments are a new feature of Android, and this brief example only scratches the surface of how Fragment will change the way Android UIs are implemented. You can see from this example that using Fragments is efficient and expressive. You could do something similar by showing and hiding View objects, but you would have the burden of making the user's interaction with a task and its back stack behave consistently. Using Fragment, and FragmentTransaction, this behavior is implemented concisely and in a standardized way, ensuring consistent behavior in applications designed for tablets.

You can read more about Fragment, and how it can be used alongside starting Activity objects to drill into detail views in an Android UI in an article by Dianne Hackborn, here.

For more about Android software development, you can now get Programming Android in an early access edition, here.

The code is in the zip file attached to this article.

Attached File  FragmentTest.zip (51.5K)
Number of downloads: 4658

Programming Android

Learn more about this topic from Programming Android.

What does it take to build well-engineered Android applications? Explore Android's core building blocks and APIs in depth with this authoritative guide, and learn how to create compelling apps that work on a full range of Android devices. You'll work with proven approaches to app design and implementation—including application frameworks that you can use as a starting point for your own projects.

See what you'll learn


Tags:
2 Subscribe


4 Replies

0
  gbroon's Photo
Posted Oct 07 2011 12:41 PM

There is a mistake in the code for FragmentTestActivity.

ArrayAdapter numbers = new ArrayAdapter<String>(getApplicationContext(),

Should be

ArrayAdapter<String> numbers = new ArrayAdapter<String>(getApplicationContext(),
0
  Yekta's Photo
Posted May 07 2012 02:05 AM

thank you zigurd ,

i realy appreciate for your answer :)
0
  Deepthimsr's Photo
Posted Jun 05 2012 01:54 AM

i getting runtime exception...
but i am unaware of this pls help me what it means!!!


AndroidRuntime(583): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.fragment.dee/com.fragment.dee.FragmentTestActivity}: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
0
  Developer in Paris's Photo
Posted Nov 22 2012 05:19 AM

Great sample source code!

thanks!

But there is a bug when you rotate the screen (mode portrait to mode landscape).

the app crash, could you explain me the mistake?

I think the issue is from the Fragment transaction...


thanks