In this part, we will be adding necessary code and resources required to add the 3 tabs – CURRENT, LOCATIONS, and MAP. Since we have only 3 tabs, we will stick with Android Design Guidelines for Tabs by using Fixed Tabs.

If you have done any UI programming in other platforms, and haven’t used Tabs in Android world before, you will be tempting to add a Tab widget to the main container. But that’s not actually what we need. What we actually need is to set the navigation mode of the ActionBar to tabs mode. Surprised?

You might be wondering whether you can just use the vanilla Tab Widget. Yes, you can. But there is a nice advantage of setting the Action Bar’s navigation mode to tabs – the tab headers get merged into the ActionBar when there is enough space available, as we will see at the end of this part. If you are curious, there are altogether 3 kind of navigation modes – Standard, List, and Tabs. You can read more about these navigation modes here.

First, we get the supported action bar and not the regular action bar because, remember, we want to make the app backward compatible. We then set the navigation mode to ActionBar.NAVIGATION_MODE_TABS. Then we create 3 tabs and add them to the action bar. Let’s call this method as soon as our activity gets created.

Unable to start activity ComponentInfo{com.ashokgelal.tagsnap/com.ashokgelal.tagsnap.DefaultActivity}: java.lang.IllegalStateException:Action Bar Tab must have a Callback …

So, the Action Bar Tab is looking for someone to handle a TabListener callback. Let’s add a new class that implements ActionBar.TabListener and, thus, will be responsible for handling tab selected, unselected, and reselected events:

Add a new class TabListener, make it implement ActionBar.TabListener, and override 3 required methods:

Before we start filling up these 3 methods, let’s write a constructor with 3 parameters – the parent context, a tag for each fragment, and a class name. Also, when we are at it, we will also try to find a fragment by given tag to see if it already exists. If it does, we can use it later without creating a new one.

Now, let’s start filling the remaining 3 methods. We won’t be handling onTabReselected() event. You can leave it as it is. We need to handle other 2 methods. Here is what we want to do: when a tab is selected, essentially, we need to set a fragment as the main content of the tab. At any time, we’ll have 1, and only 1, fragment active. So, instead of adding a fragment, we will replace the active one with a new one. But what if we already have a fragment created before? Well, in that case mFragment shouldn’t be null, and so we’ll reattach if it is currently detached. If the fragment is already the active one then the onTabSelected() will end up being a no-op. Let’s write this code.

This method is simple – if we have a fragment, we just detach it from the given fragment transaction.

This is all we need to do in TabListener class. The next task is to set it as callbacks listener for each of our 3 tabs. For each tab, set callback to a new instance of TabListener. We’ll pass 3 parameters to TabListener’s constructor – FragmentActivity (you can pass this), a unique string tag (you can pass title), and the associated fragment class itself (CurrentFragment.class, LocationsFragment.class, and MapFragment.class).

Saving the Last Selected Tab:

If you rotate your device to landscape mode, you will notice that the tab merges into the Action Bar.

This is the one of the nice benefits we get for free by setting the Action Bar’s navigation mode to tabs. However, we have a small problem here. If you select any other tabs than the CURRENT tab and then change the orientation, the app doesn’t remember the last selected tab but instead sets the CURRENT tab as selected. This happens because when a device configuration changes, Android destroys the active Activity and recreates it. This is part of the Activity Lifecycle. Fortunately, this is very easy to fix by just saving tab’s selected state before our activity gets trashed, and then restoring the state later when the activity gets recreated.

Before an activity (or a fragment) gets destroyed, Android calls the onSaveInstanceState() method, if your activity/ fragment has overriden it, passing a Bundle. We will use this bundle instance to save the selected tab index. Later, in the onCreate() method, we will check the passed bundle instance to see if contains the tab index. If it does, we retrieve the tab index, and set it as the selected tab’s position.

Run the app, select either LOCATIONS or MAP tab, and rotate your device. The app should remember the last selected tab position.

Next:

In the next tutorial we’ll add support for fetching current location, reverse geocode the location to get the actual human-readable address, and display properly formatted address in the CURRENT fragment.

nice tutorial.
I've got a question regarding the structure of an android project. Is there a best practice or a convention how I should structure the files of my project?

best regards,
BricktTop

ashokgelalon January 20, 2013 at 11:23 am

For the most parts, and esp. for resources, Android already requires a standard set of conventions. For the .java files, I've not yet seen any convention. I've seen many people just leaving all the files in the root 'src' folder. I personally like to organize files to be able to find them easily in the future. So, the convention that I follow is to put all the interfaces in 'interfaces' folder, all the model classes in 'model' folder, all the static and converter classes in one 'helpers' folder. I leave the Activities and Fragments in the root 'src' folder. I've always found it easy to find files with this layout.