Using the Android Fragment API in a Tablet App

March 18th, 2012 by

Since I got a new tablet running Android 4.0 aka ice cream sandwich I wanted to play around a bit with the fragments API and creating an application for a tablet.

In the following tutorial, we’re going to build an application that renders several articles from a popular tech blog (just kidding) in a web view.


 

What to build

We’re going to build an application that is able to display web links in the left section of the screen and when clicked, displaying the corresponding website content in the right section of the application screen.

To give you an impression of the look of the final application, here is a screenshot of the running app on my tablet.

The final application using Fragments

The final application using Fragments

Prerequisites

You need to meet the following requirements to run the following examples…

Setting up your Tablet

This one worked for my Asus EEE Transformer Prime Tablet on my Ubuntu workstation..

  1. Find out the vendor id of the Asus USB device
    user@host:~$ lsusb
    [..]
    Bus 002 Device 005: ID 0b05:4d01 ASUSTek Computer, Inc.
  2. Add a new file /etc/udev/rules.d/99-android.rules and add the following line with your vendor id from above
    SUBSYSTEM=="usb", SYSFS{idVendor}=="0b05", MODE="0666"
  3. Restart the udev service
    user@host:~$ sudo restart udev
    udev start/running, process 2774
  4. Restart your adb service
    user@host:~$ adb kill-server
  5. Hopefully your device is now correctly recognized
    user@host:~$ adb devices
    List of devices attached
    C1OKAS132708    device

Creating the App

First create a new Android project using your IDE. What we’re building here is quite easy and we’re making use of the following components:

  • An Activity that is our starting point in the application.
  • This activity renders a view that contains two Fragments .. and therefore references two fragment classes:
  • The SelectionFragment that renders a list of clickable items and another fragment ..
  • The NewsFragment that contains a simple WebView to load the selected website and renders its content as a browser
  • Our FragmentApplication object extends Application and shares common used data throughout our application such as..
  • our Article beans .. each article has an URL and a title ..

The Article Bean

The following bean is just a container for the articles to display. It is one time used to render the selection menu in the fragment and on the other side contains the URL that is used to display the corresponding website in the news fragment.

Please note that I am overriding Object’s toString method here to get an article’s title displayed in the ListView in the selection fragment.

package com.hascode.android.app;
 
public class Article {
	private final String title;
	private final String url;
 
	public Article(final String title, final String url) {
		super();
		this.title = title;
		this.url = url;
	}
 
 // getter & setter
 
	@Override
	public String toString() {
		return title;
	}
 
}

The Application Object

This is the place where we define some articles to be used by other components of our application.

package com.hascode.android.app;
 
import java.util.ArrayList;
import java.util.List;
 
import android.app.Application;
 
public class FragmentApplication extends Application {
	private final List<Article> links = new ArrayList<Article>();
	{
		links.add(new Article(
				"Selenium WebDriver, Selenium Server and PageObjects by Example",
				"http://www.hascode.com/2012/03/selenium-webdriver-selenium-server-and-pageobjects-by-example/"));
		links.add(new Article("Ordering your JUnit Rules using a RuleChain",
				"http://www.hascode.com/2012/02/ordering-your-junit-rules-using-a-rulechain/"));
		links.add(new Article(
				"JPA Persistence and Lucene Indexing combined in Hibernate Search",
				"http://www.hascode.com/2012/02/jpa-persistence-and-lucene-indexing-combined-in-hibernate-search/"));
		links.add(new Article(
				"Neo4j Graph Database Tutorial: How to build a Route Planner and other Examples",
				"http://www.hascode.com/2012/01/neo4j-graph-database-tutorial-how-to-build-a-route-planner-and-other-examples/"));
		links.add(new Article(
				"Create Mobile Websites using Java Server Faces and PrimeFaces Mobile",
				"http://www.hascode.com/2012/01/create-mobile-websites-using-java-server-faces-and-primefaces-mobile/"));
		links.add(new Article("Writing Styles and Themes on Android",
				"http://www.hascode.com/2011/12/writing-styles-and-themes-on-android/"));
		links.add(new Article(
				"Managing Background Tasks on Android using the Alarm Manager",
				"http://www.hascode.com/2011/11/managing-background-tasks-on-android-using-the-alarm-manager/"));
		links.add(new Article(
				"Finding Memory Leaks using Eclipse and the MemoryAnalyzer Plugin",
				"http://www.hascode.com/2011/11/finding-memory-leaks-using-eclipse-and-the-memoryanalyzer-plugin/"));
		links.add(new Article(
				"Testing RESTful Web Services made easy using the REST-assured Framework",
				"http://www.hascode.com/2011/10/testing-restful-web-services-made-easy-using-the-rest-assured-framework/"));
		links.add(new Article(
				"Maven Tomcat Plugin: Adding Authentication to an Embedded Tomcat",
				"http://www.hascode.com/2011/10/maven-tomcat-plugin-adding-authentication-to-an-embedded-tomcat/"));
	}
 
	public List<Article> getLinks() {
		return links;
	}
 
}

Afterwards the application should be registered in the AndroidManifest.xml

<application icon="@drawable/ic_launcher" label="@string/app_name" debuggable="true" name="FragmentApplication">
</application>

The Activity

The following activity is responsible for these simple tasks:

  • Rendering the main layout
  • Fetching the articles from the application object and passing them to the selection fragment as a generic ListAdapter
  • Responding for click events from the selection fragment and passing the URL to be opened in a WebView to the NewsFragment.
  • I have implemented the callback method as a public interface in the selection fragment to be implemented by the activity

This my activity named FragmentExampleActivity

package com.hascode.android.app;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ArrayAdapter;
 
public class FragmentExampleActivity extends Activity implements
		SelectionFragment.OnSelectListener {
	private FragmentApplication app;
 
	@Override
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
 
		app = (FragmentApplication) getApplication();
		SelectionFragment selectionFragment = (SelectionFragment) getFragmentManager()
				.findFragmentById(R.id.selection_fragment);
 
		selectionFragment.setListAdapter(new ArrayAdapter<Article>(this,
				R.layout.list_item, app.getLinks()));
	}
 
	@Override
	public void onItemSelected(final int position) {
		final Article article = app.getLinks().get(position);
		NewsFragment newsFragment = (NewsFragment) getFragmentManager()
				.findFragmentById(R.id.news_fragment);
		if (newsFragment == null) {
			Intent intent = new Intent(this, NewsFragment.class);
			intent.putExtra("url", article.getUrl());
			startActivity(intent);
		} else {
			newsFragment.updateContent(article.getUrl());
		}
 
	}
}

The activity of course needs to be registered in your AndroidManifest.xml

One speciality should be mentioned here .. we’re passing articles to the selection fragment using a ListAdapter and telling him to render the items using this item we’re specifying in res/layout/list_item.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>

Adjusting Permissions

Because we’ll be using a WebView later to display a web site, we need to add the use-permission android.permission.INTERNET to our AndroidManifest.xml – mine finally looks like this one

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hascode.android.app"
    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:debuggable="true" android:name="FragmentApplication">
        <activity
            android:label="@string/app_name"
            android:name=".FragmentExampleActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Defining the Main Layout

Now we need a main layout that should contain the two fragments .. luckily the GUI editor supports fragments ..

Fragments in Eclipse's GUI Editor

Fragments in Eclipse's GUI Editor

This is my res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/frags"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
 
    <!-- "Selection Fragment" -->
    <fragment
        android:id="@+id/selection_fragment"
        android:layout_width="@dimen/selection_fragment"
        android:layout_height="match_parent"
        class="com.hascode.android.app.SelectionFragment" />
 
    <!-- "Display/News Fragment" -->
    <fragment
        android:id="@+id/news_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.hascode.android.app.NewsFragment" />
</LinearLayout>

As you can see it references two fragment classes named SelectionFragment and NewsFragment .. we’ll be implementing them both in the next step!

The Selection Fragment

The selection fragments sole purpose is to display a list of links and call the activity’s callback method when a click on an item is received.

Because we’re a lazy bunch of programmers, we’re extending ListFragment instead of Fragment to get a fully functional ListView and need to implement less methods..

package com.hascode.android.app;
 
import android.app.Activity;
import android.app.ListFragment;
import android.view.View;
import android.widget.ListView;
 
public class SelectionFragment extends ListFragment {
	private OnSelectListener onSelectListener;
 
	public interface OnSelectListener {
		public void onItemSelected(final int position);
	}
 
	@Override
	public void onAttach(final Activity activity) {
		super.onAttach(activity);
		onSelectListener = (OnSelectListener) activity;
	}
 
	@Override
	public void onListItemClick(final ListView l, final View v,
			final int position, final long id) {
		onSelectListener.onItemSelected(position);
	}
 
}

The News/Browser Fragment

This fragment does only one thing: Creating a browser and displaying a web site’s content.

In the first step, we’re defining the layout for this fragment in res/layout/news_fragment.xml

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

Afterwards we create the fragment class.

package com.hascode.android.app;
 
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
 
public class NewsFragment extends Fragment {
	public void updateContent(final String url) {
		WebView webView = (WebView) getActivity().findViewById(R.id.webView);
		// my blog is using javascript
		WebSettings settings = webView.getSettings();
		settings.setJavaScriptEnabled(true);
		webView.loadUrl(url);
	}
 
	@Override
	public View onCreateView(final LayoutInflater inflater,
			final ViewGroup container, final Bundle savedInstanceState) {
		return inflater.inflate(R.layout.news_fragment, container, false);
	}
}

Running the Application

Now we’re ready to run the application .. it could possibly look like this screencast (made with androidscreencast).

Tutorial Sources

I have put the source from this tutorial on my Bitbucket repository – download it there or check it out using Mercurial:

hg clone https://bitbucket.org/hascode/android-fragment-app

Resources

package com.hascode.android.app;

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

import android.app.Application;

public class FragmentApplication extends Application {
private final List<Article> links = new ArrayList<Article>();
{
links.add(new Article(
“Selenium WebDriver, Selenium Server and PageObjects by Example”,
“http://www.hascode.com/2012/03/selenium-webdriver-selenium-server-and-pageobjects-by-example/”));
links.add(new Article(“Ordering your JUnit Rules using a RuleChain”,
“http://www.hascode.com/2012/02/ordering-your-junit-rules-using-a-rulechain/”));
links.add(new Article(
“JPA Persistence and Lucene Indexing combined in Hibernate Search”,
“http://www.hascode.com/2012/02/jpa-persistence-and-lucene-indexing-combined-in-hibernate-search/”));
links.add(new Article(
“Neo4j Graph Database Tutorial: How to build a Route Planner and other Examples”,
“http://www.hascode.com/2012/01/neo4j-graph-database-tutorial-how-to-build-a-route-planner-and-other-examples/”));
links.add(new Article(
“Create Mobile Websites using Java Server Faces and PrimeFaces Mobile”,
“http://www.hascode.com/2012/01/create-mobile-websites-using-java-server-faces-and-primefaces-mobile/”));
links.add(new Article(“Writing Styles and Themes on Android”,
“http://www.hascode.com/2011/12/writing-styles-and-themes-on-android/”));
links.add(new Article(
“Managing Background Tasks on Android using the Alarm Manager”,
“http://www.hascode.com/2011/11/managing-background-tasks-on-android-using-the-alarm-manager/”));
links.add(new Article(
“Finding Memory Leaks using Eclipse and the MemoryAnalyzer Plugin”,
“http://www.hascode.com/2011/11/finding-memory-leaks-using-eclipse-and-the-memoryanalyzer-plugin/”));
links.add(new Article(
“Testing RESTful Web Services made easy using the REST-assured Framework”,
“http://www.hascode.com/2011/10/testing-restful-web-services-made-easy-using-the-rest-assured-framework/”));
links.add(new Article(
“Maven Tomcat Plugin: Adding Authentication to an Embedded Tomcat”,
“http://www.hascode.com/2011/10/maven-tomcat-plugin-adding-authentication-to-an-embedded-tomcat/”));
}

public List<Article> getLinks() {
return links;
}

}

Tags: , , , , ,

Leave a Reply

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 81,008 bad guys.

Search
Categories