Using the Android Fragment API in a Tablet App
March 18th, 2012 by Micha KopsSince 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.
Contents
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.
Prerequisites
You need to meet the following requirements to run the following examples…
- Java Development Kit 6
- Android SDK
- An AVM with at least Android 4.0.3 / API Version 15
- I am using Eclipse with the ADT Plugin installed here and I’m running the application on my Asus EEE Transformer Prime tablet
Setting up your Tablet
This one worked for my Asus EEE Transformer Prime Tablet on my Ubuntu workstation..
- Find out the vendor id of the Asus USB device
user@host:~$ lsusb [..] Bus 002 Device 005: ID 0b05:4d01 ASUSTek Computer, Inc.
- 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"
- Restart the udev service
user@host:~$ sudo restart udev udev start/running, process 2774
- Restart your adb service
user@host:~$ adb kill-server
- 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", "https://www.hascode.com/2012/03/selenium-webdriver-selenium-server-and-pageobjects-by-example/")); links.add(new Article("Ordering your JUnit Rules using a RuleChain", "https://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", "https://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", "https://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", "https://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", "https://www.hascode.com/2011/12/writing-styles-and-themes-on-android/")); links.add(new Article( "Managing Background Tasks on Android using the Alarm Manager", "https://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", "https://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", "https://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", "https://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 ..
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) on YouTube here.
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
Article Updates
- 2018-06-01: Embedded YouTube video removed (GDPR/DSGVO).
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”,
“https://www.hascode.com/2012/03/selenium-webdriver-selenium-server-and-pageobjects-by-example/”));
links.add(new Article(“Ordering your JUnit Rules using a RuleChain”,
“https://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”,
“https://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”,
“https://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”,
“https://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”,
“https://www.hascode.com/2011/12/writing-styles-and-themes-on-android/”));
links.add(new Article(
“Managing Background Tasks on Android using the Alarm Manager”,
“https://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”,
“https://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”,
“https://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”,
“https://www.hascode.com/2011/10/maven-tomcat-plugin-adding-authentication-to-an-embedded-tomcat/”));
}
public List<Article> getLinks() {
return links;
}
}