Tab Layout

A TabActivity and its TabHost view are the Android equivalent of the iPhone UITabBarController. I created this app from Android’s tab tutorial. The indicators (labels) start at 1 because human beings like to count starting at 1, but Android counts the tabs starting at 0. Tab 1 (using Android’s counting scheme) has an icon and Tab 2 is initially the current tab.

This main Activity of this app has the attribute android:theme="@android:style/Theme.NoTitleBar" in AndroidManifest.xml. This prevented the tabs from appearing in the ActionBar, and allowed the tab icon(s) to appear. The snapshot of ApiDemos/Views/Tabs shown below does not have this attribute. The tabs therefore appeared in the ActionBar, and the icons were not shown. See the next example for icons in the ActionBar tabs, and for avoiding the now-deprecated class TabActivity.

Each tab launches a different activity. The main activity must be derived from class TabActivity (not from class plain old Activity), which has a getTabHost method. The TabHost view contains two subviews: a TabWidget containing the tabs and a FrameLayout containing the views that are presented by the tabs. (We saw FrameLayout here.) If you need them, these two subviews are returned by the getTabWidget and getTabContentView methods of the TabHost.

Source code in Tab.zip

  1. MyTabActivity.java
  2. Tab1Activity.java
  3. Tab2Activity.java
  4. Tab3Activity.java
  5. main.xml: a TabHost containing a TabWidget and a FrameLayout. The id numbers (android.R.id.tabs) and (android.R.id.tabcontent) do not have plusses.
  6. ic_tab2.xml contains a State List: selected and unselected.
  7. ic_tab2_grey.png: selected, 32 × 32 pixels.
  8. ic_tab2_white.png: unselected.
  9. AndroidManifest.xml: the activity element has the attribute android:theme="@android:style/Theme.NoTitleBar".

Create the project

  1. The name TabActivity is already taken, so I named my subclass MyTabActivity.
  2. I took the pair of icons from the tab tutorial; see Tab Icons.
  3. I created the file ic_tab2.xml in the folder res/drawable.
  4. The first Activity must be a subclass of TabActivity. The other three Activities should be subclasses of Activity.
  5. To create the three other Activities, go to the Package Explorer and highlight the package in the src folder.
    File → New → Class
    Superclass: android.app.Activity
    ✓ Inherited abstract methods
  6. Add the three extra activity elements to the application element in AndroidManifest.xml.
  7. The tabs look better without the title bar. Add the attribute
    android:theme="@android:style/Theme.NoTitleBar"
    
    to the first activity element in AndroidManifest.xml.

Things to try

  1. Create the tabs with a loop in onCreate.
    		final Class<?>[] c = { //an array of Class objects.  "class" is a Java keyword.
    			Tab1Activity.class,
    			Tab2Activity.class,
    			Tab3Activity.class
    		};
    
    		for (int i = 0; i < c.length; ++i) {
    			TabHost.TabSpec tabSpec = tabHost.newTabSpec(String.format("tag%d", i + 1));
    			tabSpec.setIndicator(String.format("Tab%d", i + 1));
    			tabSpec.setContent(new Intent(this, c[i]));
    			tabHost.addTab(tabSpec);
    		}
    

  2. Chain the method calls together.
    			tabSpec.setIndicator(String.format("Tab%d", i + 1))
    				.setContent(new Intent(this, c[i]));
    

  3. Have Tab1Activity create a ToggleButton instead of a TextView. The newly-created ToggleButton is initialized to off.
    		ToggleButton toggleButton = new ToggleButton(this);
    		setContentView(toggleButton);
    
    Observe that when you go to another tab and come back to tab 1, the state of the ToggleButton is preserved. This means that you are returning to the same Tab1Activity object.

    Now add a flag to the Intent object before installing it into tab 1.

    		intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    
    Observe that the onCreate onCreate method of Tab1Activity is now called every time you go back to tab 1. The ToggleButton is always off when it reappears.

  4. How many tabs do we have room for?

  5. Give the TabHost an onTabChangeListener that will detect when a tab has been selected.

  6. The iPhone tabs are always at the bottom. To get the Android tabs at the bottom, write the TabWidget below the FrameLayout in the LinearLayout in main.xml. Give the following properties to the FrameLayout.
    	android:layout_height="wrap_content"
    	android:layout_weight="1"
    
    Be sure to keep the names of the id properties tabs and tabcontent.

  7. Comment out the setContentView in MyTabActivity. Why do we still see the picture? And why don’t the android:ids have plusses?

Tabs in ApiDemos

  1. Views/Tabs: Tabs1.java. Uses the deprecated class TabActivity. No ActionBar or Fragments.

  2. App/Action Bar/Action Bar Tabs. Press the “Toggle tab mode” button first. The ActionBar holds tabs. Each tab holds a TabListener. Each TabListener holds a Fragment. Each Fragment holds a View. ActionBarTabs.java, action_bar_tabs.xml, action_bar_tab_content.xml

  3. App/Fragment/Tabs: FragmentTabs.java

Tabs in ApiDemos/Views/Tabs

The onCreate in the following ApiDemos does not call setContentView. Instead, we inflate the layout in the .xml file with the Activity’s LayoutInflater. The inflator uses the inflated layout to fill up the tabhost’s empty tab content view.

The third ApiDemo has multiple Activities.

  1. Content By Id. The content of each tab is a TextView in an .xml file.
    1. Tabs1.java
    2. tabs1.xml: three TextViews in a FrameLayout.
    3. strings.xml
    4. colors.xml: blue, red, green.

  2. Context By Factory. The content of each tab is a TextView created by the Activity. The Activity implements the interface TabHost.TabContentFactory containing the method createTabContent.
    1. Tabs2.java
    2. star_big_on.png (high). See the Icon Design Guidelines.
    3. star_big_on.png (medium)

  3. Content By Intent. The content of each tab is created by a separate Activity object. Each Activity object may be of a different subclass of class Activity. Construct an Intent object and tell it what subclass of Activity you want to create. Pass the Intent object to the setContent method of the TabHost.TabSpec.
    1. Tabs3.java
    2. List1.java is a ListActivity with a ListView and an ArrayAdapter<String>.
    3. Cheeses.java, used by class List1.
    4. List8.java is a ListActivity with a ListView and a PhotoAdapter (defined in the same file).
      1. list_8.xml
      2. sample_thumb_0.png
      3. sample_thumb_1.png
      4. sample_thumb_2.png
    5. Controls1.java: the destroy tab instantiates a plain old Activity with lots of widgets.
      1. controls_1.xml

Unchain the method calls in Content by Id

	LayoutInflater.from(this).inflate(R.layout.tabs1, tabHost.getTabContentView(), true);
	LayoutInflater inflater = LayoutInflater.from(this);
	inflater.inflate(R.layout.tabs1, tabHost.getTabContentView(), true);
	TabHost tabHost = getTabHost();

	tabHost.addTab(tabHost.newTabSpec("tab1")
		.setIndicator("tab1")
		.setContent(R.id.view1));
	TabHost tabHost = getTabHost();
	TabHost.TabSpec tabSpec = tabHost.newTabSpec("tab1");

	tabSpec.setIndicator("tab1");
	tabSpec.setContent(R.id.view1);
	tabHost.addTab(tabSpec);