http://developer.android.com/resources/tutorials/views/hello-tabwidget.html
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html
But there might come a certain point where using the TabHost becomes cumbersome, at least when it comes to the point of customizing the TabWidget used by the TabHost (to represent the actual tabs). Sure, there are some great tutorials out there showing how to completely customize the TabWidget, but nevertheless it proves rather difficult to completely accomplish ones needs. It's also possible to completely abandon the use of the TabHost and try another approach, even though there might be good reasons to use the TabHost. Just why is the TabHost so stiff?
Well there is a satisfiable solution for this problem: Just abandon the TabWidget and just use the TabHost "manually". In this case "abandon" means to set its visibility to "gone" and "manually" means handling tab transitions by code, i.e. using the appropriate methods given by the TabHost.
The results are:
- Complete freedom in creating your very own tab navigation
- Complete freedom in using tab management to add/remove tabs as you like and hide it from the user
- Using the advantages of the TabHost without even using a tabbed ui approach
"Advantages" of using the TabHost isn't applying for all kind of apps, but if you are certain that it's very appropriate for the content of your app to be processed by a tab like approach, these arguments might facilitate your decision to use the TabHost:
- Spending less time for carrying about the life cycle of your activities
- Very clear navigation structure,; you can centralize all transitions of your activities into a single component (e.g. your central TabActivity)
- Instantiation of your activities is always done automatically by demand, i.e. by switching to the tab; no need to write any code for this at all
- All stuff is basically done within a single activity (your TabActivity), it's very clear what's going on within the app
There are certainly more pros and also some cons. One of it is the fact, that your app takes more space in memory and your layout tree can grow larger because you always have two more FrameLayouts (the TabHost, which is a FrameLayout, and the @android:id/tabcontent element, which is also often a FrameLayout) between your app content and the main window as without the tab approach. So it could be necessary to spend some time thinking about disposing resources to keep performance high.
Now it's time to have a quick look at how to accomplish the idea of a more flexible TabHost.
The layout resource looks very simple:
As mentioned before the TabWidget is set to "gone", so it won't bother anymore.<?xml version="1.0" encoding="utf-8"?> <TabHost xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TabWidget android:id="@android:id/tabs" android:layout_width="fill_parent" android:layout_height="wrap_content" android:visibility="gone"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="fill_parent" android:layout_height="fill_parent"/> </TabHost>
The ui for the tabs can be done just as you wish. For this example I chose a navigation by the build in menu button. But there is also the possibility to do it by radiobuttons or a list in the home screen or dialog appearing on a button press or a gesture detector or etc. just anything.
So let's look inside our main TabActivity, which will provide the menu and the necessary tab switch mechanics.
That's all the code for creating and switching to the tabs. Why use Activities as tabs instead of Views? There are some benefits:public class Main extends TabActivity { // indices for each tab public static final int HOME_TAB = 0; public static final int NEWS_TAB = 1; // ... etc. ... // helpful indicators for which tab was selected public static int SELECTED_NEWS_TAB = NEWS_TAB; public static int SELECTED_BLOGS_TAB = BLOGS_TAB; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); setTabs(); } /** * Delegate tab creation and adding. */ private void setTabs() { // add the necessary tabs addTab(R.string.tag_home, HomeActivity.class); addTab(R.string.tag_news, NewsActivity.class); // ... etc. ... } /** * Create a tab as an Activity and add it to the {@link TabHost} * * @param tagId * resource id of the tag for finding the tab * @param activity * the activity to be added */ private void addTab(int tagId, Class<?> activity) { // create an Intent to launch an Activity for the tab (to be reused) Intent intent = new Intent().setClass(this, activity); // initialize a TabSpec for each tab and add it to the TabHost TabHost.TabSpec spec = getTabHost().newTabSpec(getString(tagId)); spec.setContent(intent); spec.setIndicator(getString(tagId)); getTabHost().addTab(spec); } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); // find which menu item has been selected switch (item.getItemId()) { // check for each known menu item case (R.id.menu_home): getTabHost().setCurrentTab(HOME_TAB); break; case (R.id.menu_news): // check which tab to be selected if (SELECTED_NEWS_TAB == SINGLENEWS_TAB) { getTabHost().setCurrentTab(SINGLENEWS_TAB); } else { getTabHost().setCurrentTab(NEWS_TAB); SELECTED_NEWS_TAB = NEWS_TAB; } break; // ... tab switching ... } return true; } }
- Use separate menus for each Activity; this won't be as easy when using Views (check for which view is actually visible)
- When using Fragments, it's easier to manage them in separate Activities instead of one huge TabActivity
That's why I prefer to use an Activities for each tab.
Each menu item provides an icon and a text description for each tab.<menu xmlns:android="http://schemas.android.com/apk/res/android" android:name="Menu"> <item android:id="@+id/menu_home" android:title="@string/menu_home" android:icon="@drawable/tab_home"> </item> <item android:id="@+id/menu_news" android:title="@string/menu_news" android:icon="@drawable/tab_news"> </item> <!-- ... etc. ... --> </menu>
At last the basic structure of the Activities used for this example:
To have less boilerplate code, having a basic Activity that does setup and closing logic makes sense for this example.public abstract class MyAbstractActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContent(); } protected void setContent() { setContentView(R.layout.content); ((TextView) findViewById(R.id.content)).setText(getContent()); } protected abstract String getContent(); @Override public void onBackPressed() { AlertDialog.Builder dialog = new AlertDialog.Builder(this).setMessage("Close app?").setCancelable(false) .setPositiveButton("YES", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }).setNegativeButton("No", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.cancel(); } }); dialog.create().show(); } }
A very rudimentary example of a concrete Activity:
Layout:
Code:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/content" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="Here comes the tab content" android:textColor="#FFEEEEEE"/> </LinearLayout>
A more complex example of an Activity with tab switching mechanics:public class AboutActivity extends MyAbstractActivity { @Override protected String getContent() { return "About info"; } }
Layout:
Code:<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <TextView android:id="@+id/content" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Here comes the tab content" android:textColor="#FFEEEEEE"/> <Button android:id="@+id/bttn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ClickMe"/> </LinearLayout>
Switching mechanics can also be externalized into a separate Singleton that has a reference to the TabHost.public class BlogsActivity extends MyAbstractActivity { @Override protected void setContent() { setContentView(R.layout.content_bttn); ((TextView) findViewById(R.id.content)).setText(getContent()); ((Button) findViewById(R.id.bttn)).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (getParent() instanceof Main) { ((Main) getParent()).getTabHost().setCurrentTab(Main.SINGLEBLOG_TAB); Main.SELECTED_BLOGS_TAB = Main.SINGLEBLOG_TAB; } } }); } @Override protected String getContent() { return "Fancy blogs list"; } }
This ends the example of how to use the TabHost a bit more freely.
Icons from http://www.icojoy.com
Complete Android Project for Eclipse: CustomTabsDemo