Category Archives: Android

Following last weeks announcement of Android 4.0 (Ice Cream Sandwich) and the release release of the 4.0 SDK, Google/Android also release a new version of the Compatibility Library (r4). Go check it out, it promises some bug fixes and additional support for ICS.

In my previous two posts, I explained how you would implement Tabs using Fragments and then how to implement page swiping using ViewPager. In this post, I’ll bring those two nuggets together and show you how to implement Swipe-able Tabs (i.e switch between tabs using the swipe gesture).

* Caveat Alert * At the time of this posting, there is no way of implementing MapView as a fragment, which may put a spanner in the works if you’re thinking about using this UI pattern with a MapView.

Requirements

To implement a Tabs & ViewPager, using fragments on devices running Android 2.1 or higher, you’ll need to include the Android Compatibility library. In my example, I’m using Compatibility library v4.

Step-by-step

Define the Tab ViewPager layout

Define the PagerAdapter

Define the Tab FragmentActivity

The Code

The Tab ViewPager layout

The Tab ViewPager layout (tabs_viewpager_layout.xml) declares a TabHost and child TabWidget views as per normal. For this implementation, instead of having the dummy FrameLayout to hold the content, we define the ViewPager (android.support.v4.view.ViewPager)

Define the PagerAdapter

As explained in the previous post, Page Swiping using ViewPager, the PagerAdapter is responsible for creating/returning the appropriate Fragment to the ViewPager. The declaration of PageAdapter is unchanged from the previous post.

Define the Tab FragmentActivity

The Tab FragmentActivity is a modified version Tab FragmentActivity I posted about in, Tabs, The Fragment Way. Thankfully, using ViewPager requires less code =)

*Disclaimer: The redundant lines of code are there to illustrate the minimal amount of changes required to implement view paging from the stock Tab implementation*

Change 1: As per the normal initialisation of Tabs, we create Tahhost.TabSpec instances for each tab. In this implementation, we simple just add it to the TabHost (line 139) without having to detach fragments.

Change 2: Initialise the ViewPager. From line 099, we instantiate the Fragments and pass them to the ViewPager via the PagerAdapter. The order that the Fragments are supplied in the List will determine their tab order.

Change 3: Tab FragmentActivity to implement ViewPager.OnPageChangeListener interface. In order to handle page “swipe” events, our Tab FragmentActivity needs to implement the ViewPager.OnPageChangeListener interface. For our purpose we only really need to implement the onPageSelect() method (Line 168). Simply, when the page is selected (paged), it’s associated Tab is selected.

Change 4: Modify the onTabChanged() method. Similarly to Change 3, when a tab is select (and therefore becomes selected), we set the appropriate Fragment via the ViewPager mViewPager (Line 148).

Defining the FragmentActivty

Our main FragmentActivity is going to host the ViewPager layout viewpager_layout.xml and initialise the ViewPager with an adapter that managers the fragments that are displayed when the user swipes between pages. In my implementation, I simply instantiate the Fragments upfront and supply them in a list to the

constructor of the PagerAdapterPagerAdaptor.java.

As I mentioned in the intro, I reused the fragment implementations from my previous Fragment post where fragment 1 is Red, fragment 2 is Green and fragment 3 is Blue. After all, the design goal of Fragments is code/UI reuse.

If you’re the die-hard follower (yes, I said “the” because the stats says there is) of this blog, you would have probably noticed that I’ve been M.I.A for awhile. We’ll, it’s mainly been because I’ve been working on an Android project with a super-toit deadline which we managed to launch JIT on to the Marketplace.

Now that the first phase of that project is over, I’m now able to reflect a bit on the code that was produced and share here some of my experiences and (source).

In this first installment, I want to illustrate how to create a Tab activity using Fragments since, going-forward, it is suggested that building on Fragments will ensure your app is compatible with pre-Honeycomb, Honeycomb and Ice Cream Sandwich (tablet and phone) OS versions (see this article). Remember, in Android 2.x Tabs are presented as classic “filing” tabs, while on Android 3.x and higher tabs are represented in the ActionBar UI component.

Requirements

To implement a Tabbed, using fragments on devices running Android 2.1 or higher, you’ll need to include the Android Compatibility library. In my example, I’m using Compatibility library v4

Step-by-Step

Define TabHost layout

Define Tab fragment layouts

Define Tab fragments

Define main fragment activity

The Code

Define the TabHost layout

The tabbed UI layout consists of 4 parts: TabHost, TabWidget, FrameLayout and the content layout. tabs_layout.xml illustrates how they stack up. You’ll notice that the FrameLayout id=realtabcontent is a child of a FrameLayout. Isn’t this redundant? The answer is no, be attaching out fragments to this FrameLayout.

Define Tab fragment layouts

Next, we define out fragment layouts (i.e the content layout for each tab). Nothing special here…you’d define your layout as if the layout was for a stand-alone activity. Below is tab_frag1_layout.xml (tab_frag2_layout.xml and tab_frag3_layout.xml are exactly the same except the have different colours specified for their backgrounds).

Define Tab fragment classes

Each tab content fragment needs to extend Fragment and inflate it’s corresponding layout. As you’ll see later, each fragment is instantiated by the main FragmentActivity using the fragment manager. Below is the definition of TabFragment1.java (TabFragment2.java and TabFragment3.java are exactly the same, except they inflate their respective layouts)

package com.andy.fragments.tabs;
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.LinearLayout;
import com.andy.R;
/**
* @author mwho
*
*/
public class Tab1Fragment extends Fragment {
/** (non-Javadoc)
* @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
*/
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (container == null) {
// We have different layouts, and in one of them this
// fragment's containing frame doesn't exist. The fragment
// may still be created from its saved state, but there is
// no reason to try to create its view hierarchy because it
// won't be displayed. Note this is not needed -- we could
// just run the code below, where we would create and return
// the view hierarchy; it would just never be used.
return null;
}
return (LinearLayout)inflater.inflate(R.layout.tab_frag1_layout, container, false);
}
}

Define the main FragmentActivity

Next, we look at onCreate(…). This is the starting point of our activity. The first step is to inflate the tabbed layout as defined in tabs_layout.xml. In step 2, we initialise the tabs. This involves invoking setup() on the TabHost view, adding tabs, save some extrinsic tab info (TabInfo) into a map and then setting the first tab content as active.

I created a static helper method to add the tabs to the TabHost as defined by an instance of TabHost.TabSpec. Each TabSpec is initialised with an instance of TabFactory (TabFactory is an inner class that extends TabContentFactory that creates an empty View as a placeholder for our fragments). Next, we detach the Fragement associated with the tab we’re trying to add. Then, finally we add the TabSpec to the TabHost.

Finally, we need to handle the onTabChanged(…) event handler where we’ll create, attach or detach the fragments when a specific tab is clicked. When onTabChanged(…) is invoked a the tab’s tag value is passed as a parameter. We use this tag value to look up the TabInfo instance we stored in initialiseTabHost(…), which has a reference to the fragment associated with the implied tab.

Our responsibility here is to detach the fragment for the tab we’re moving from, to the fragment for the tab that was clicked and to do nothing if the user clicked the active tab.

If the tab’s fragment doesn’t exist, it’s created using reflection on fragmentName.class on the FragmentManager by adding the fragment to the FrameLayout id=realcontent.