MonoAndroid: Using TabHost in your mobile applications
TabHost control in MonoAndroid
Introduction
This is third article in series of MonoAndroid
, the previous two articles are generic
BaseAdapter and android Grid View. In this article I am going to demonstrate use
of TabHost
control, its different from TabLayout provided by android mobile OS.
TabHost provided flexibility of managing data in different views by providing tabbed interface.
TabHost basically a container, where you need to put tab widget, which will host tabbed interface and FrameLayout, within which the different views are presented. Here have a look
In this article we will create Indian FlimStar Biography app based on TabHost
Step By Step we move forward
`- Create Android application by selecting New ->Solutions and
provide its name “
TabHostDemo
”
Figure 1: Creating Android Project! - Once Project is created, Open Resource Folder->Layout and Add
new file of type tabItemLayout.axml
Figure 2: Selecting New File!Figure 3: Select Layout file
- Add Following code in layout file
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="25px" android:minHeight="25px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/scrollView1"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:minWidth="25px" android:minHeight="25px"> <LinearLayout android:orientation="horizontal" android:minWidth="25px" android:minHeight="25px" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout1"> <ImageView android:src="@android:drawable/ic_menu_gallery" android:layout_width="wrap_content" android:layout_height="fill_parent" android:id="@+id/ivFilmStar" android:adjustViewBounds="true" /> <LinearLayout android:orientation="vertical" android:minWidth="25px" android:minHeight="25px" android:layout_width="match_parent" android:layout_height="fill_parent" android:id="@+id/linearLayout2" android:layout_marginLeft="7.3dp"> <TextView android:text="Large Text" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/txtFilmStar" /> </LinearLayout> </LinearLayout> <TextView android:text="Small Text" android:textAppearance="?android:attr/textAppearanceSmall" android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/txtFilmStarBio1" /> <TextView android:text="Small Text" android:textAppearance="?android:attr/textAppearanceSmall" android:layout_width="fill_parent" android:layout_height="match_parent" android:id="@+id/txtFilmStarBio2" /> </LinearLayout> </ScrollView>
Let me explain how exactly I created this layout file- Change base layout from
LinearLayout
toScrollView
, Since Biography may contain lot of text, so its better to use ScrollView. - Add
LinearLayout
with “Vertical”orientation
- Then Add
LinearLayout
with "Horizontal" orientation and add Image view and TextView in it. - Add two TextView for FilmStar Biography
- Rest I have done little tweaking for formatting
ImageView
AndTextView
See how it look like in XamarinTm Studio Designer, once all tweaking is done :-Figure 4: Designer view of GridView custom item
- Change base layout from
- Now add Activity based on above layout file (
flimStarUI.cs
), and add following code in it :-protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); SetContentView (Resource.Layout.tabItemLayout); // Create your application here }
Here we associated the flimStarUI with tabItemLayout.axml, using SetContentView and passing resource id of layout file.
- Now add class by name FlimStarInformationModal and inherit from abstract generic
class
Java.Lang.Object,
here is code for FlimStarInformationModal class. (Similar to Step2, instead of adding Layout file, add C# class file)public class FlimStarInformationModal : Java.Lang.Object { public string FilmStarName {get;set;} public int FlimStarImageID {get;set;} public string FilmStarBio1 {get;set;} public string FilmStarBio2 {get;set;} }
Each object of above class will act as contentTabLayout
. - Now in Main.axml layout file, code look like as follow:-
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/myButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <TabHost android:minWidth="25px" android:minHeight="25px" android:layout_width="fill_parent" android:layout_height="match_parent" android:id="@+id/tabHost1"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:id="@+id/linearLayout1"> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="69.3dp" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </TabHost> </LinearLayout>
- Rename Button name to
"Create TabHost Control"
, On button click we will create TabHost on runtime. - Now, Add TabHost control on your layout file.
- By default, it would add TabWidget and Frame Layout under TabHost container.
Dictionary<string,FlimStarInformationModal> dictFlimStarInfo = new Dictionary<string, FlimStarInformationModal>(); protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); // Set our view from the "main" layout resource SetContentView (Resource.Layout.Main); Button button = FindViewById<Button> (Resource.Id.myButton); button.Click += delegate { OnCreateTabHostControl(bundle); }; CreateFlimStarInformationDict (); } void CreateFlimStarInformationDict() { dictFlimStarInfo.Add ("Amitabh", new FlimStarInformationModal () { FilmStarName = "Amitabh Bachan", FlimStarImageID = Resource.Drawable.amitabh, FilmStarBio1 = "Amitabh Harivansh Bachchan (born 11 October 1942) is an Indian film actor. He first gained popularity in the early 1970s as the \"angry young man\" of Hindi cinema, and has since appeared in over 180 Indian films in a career spanning more than four decades. Bachchan is widely regarded as one of the greatest and most influential actors in the history of Indian cinema.So total was his dominance of the movie scene in the 1970s and 1980s that the French director Francois Truffaut called him a \"one-man industry\".", FilmStarBio2 = "shares with Kamal Hassan and Mammootty), a number of awards at international film festivals and award ceremonies and fourteen Filmfare Awards. He is the most-nominated performer in any major acting category at Filmfare, with 39 nominations overall. In addition to acting, Bachchan has worked as a playback singer, film producer and television presenter. He also had a stint in politics in the 1980s. The Government of India honoured him with the Padma Shri in 1984 and the Padma Bhushan in 2001 for his contributions towards the arts.", }); dictFlimStarInfo.Add ("Shahrukh", new FlimStarInformationModal () { FilmStarName = "Shahrukh Khan", FlimStarImageID = Resource.Drawable.SRK, FilmStarBio1 = "Shahrukh Khan (born 2 November 1965), often credited as Shah Rukh Khan and informally referred as SRK, is an Indian film actor. Referred to in the media as \"Badshah of Bollywood\", \"King Khan\" and \"King of Romance\", Khan has acted in 75 Hindi films in genres ranging from romantic dramas to action thrillers.[4][5][6][7] His contributions to the film industry have garnered him numerous achievements, including fourteen Filmfare Awards from thirty nominations. His eighth Filmfare Best Actor Award win made him the most awarded Bollywood actor of all time in that category, tied only with actor Dilip Kumar. In 2005, the Government of India honoured him with the Padma Shri for his contributions towards Indian cinema.", FilmStarBio2 = "Khan is considered to be one of the biggest film stars in cinematic history, with a fan following claimed to number in the billions; in 2011, the Los Angeles Times called him \"the world's biggest movie star.\"[19] He has also been regularly featured in the listing of the most powerful names in Indian Cinema and in 2008, Newsweek named him one of the 50 most powerful people in the world.[5] Khan has an estimated net worth of over US $600 million(INR25 billion).", }); dictFlimStarInfo.Add ("Hrithik", new FlimStarInformationModal () { FilmStarName = "Hrithik Roshan", FlimStarImageID = Resource.Drawable.Hrithik, FilmStarBio1 = "Hrithik Roshan (born on 10 January 1974) is an Indian film actor and professional dancer.[1][2] Having appeared as a child actor in several films throughout the 1980s, Roshan made his film debut in a leading role in Kaho Naa... Pyaar Hai in 2000. His performance in the film earned him Filmfare Awards for Best Actor and Best Male Debut. He followed it with leading roles in Fiza and Mission Kashmir (both released in 2000) and a supporting part in the blockbuster Kabhi Khushi Kabhie Gham (2001)", FilmStarBio2 = "Following through with several unnoticed performances from 2002 to 2003, he starred in the blockbusters Koi... Mil Gaya (2003) and its sequel Krrish (2006), both of which won him numerous Best Actor awards.[3] Roshan received his third Filmfare Award for Best Actor in 2006 for his performance in the action film Dhoom 2, and his fourth for Jodhaa Akbar[4] for which he was also awarded at the Golden Minbar International Film Festival. He later received further acclaim for his work in Guzaarish (2010), Zindagi Na Milegi Dobara (2011) and Agneepath (2012), his biggest commercial success so far. These accomplishments have established him as a leading contemporary actor of Hindi cinema.", }); }
- Add private dictionary where string is act as key and FlimStarInformationModal as value.
- On Button Click event, Add OnCreateTabHostControl which will create TabHost on the fly
- Add CreateFlimStarInformationDict method, which contain our dummy data, to be used while displaying data in tab.
- I have added following images under Resources->Drawable
Amitabh Bachchan Shahrukh Khan Hrithik Roshan
- Rename Button name to
- Now it time add following code to create TabHost and attaching the tabpages with it. I will
explain code step by step below:-
LocalActivityManager localActMgr = new LocalActivityManager (this, false); localActMgr.DispatchCreate (bundle); TabHost tabHost = FindViewById<tabhost> (Resource.Id.tabHost1); tabHost.Setup (localActMgr); TabHost.TabSpec tabSpec = null; Intent intent = new Intent (); intent.SetFlags (ActivityFlags.NewTask); intent.SetClass(this,typeof(flimStarUI)); tabSpec = tabHost.NewTabSpec ("Amitabh"); tabSpec.SetContent (intent); tabSpec.SetIndicator ("Amitabh"); tabHost.AddTab (tabSpec);
- First create
LocalActivityManager
object, which will used byTabHost
to setup itself, with this step, app will throw exception. - Now, find
TabHost
control usingFindViewById
method and callSetup
method by passingLocalActivityManager
Object. - Now add Intent object, type of
flimStarUI
, which will set content of tab page - Now fill
TabHost.TabSpec
with intent content and label indicator as string e.g. "Amitabh" above. - Add tab in TabHost container using AddTab method. Tab Button would automatically created in TabWidget with Indicator string as label.
- First create
- Now Build and Run the application, on Clicking "On TabHost Control" button,
you can see following, Volia!, Our Biography app is not working!.
Figure "FirstRun"
Now our raw TabHost container visible on screen, with tabbed button containing label of Actor's name. However no data in content pages, except the dummy screen we created for
flimStarUI
screen. - Now, for sending information from Main Activity to tabbed content, we have use
BroadReceiver
derived class to start communication between activities. So I have added following class :-public class MainActivityBroadcast : BroadcastReceiver { public event Action<Context,Intent> actionRecv; #region implemented abstract members of BroadcastReceiver public override void OnReceive (Context context, Intent intent) { if (this.actionRecv != null) { this.actionRecv (context, intent); } } #endregion }
- Add following code in
flimStarUI
to register and start receiving events across the application.
Add Following class variables in the class.MainActivityBroadcast broadcastRecv; const string ACTION_NEW_TABSELECTED = "NEWTABSELECTED";
InOnCreate
method add following code :-broadcastRecv = new MainActivityBroadcast (); broadcastRecv.actionRecv += OnBroadCastReceived; RegisterReceiver(broadcastRecv, new IntentFilter(ACTION_NEW_TABSELECTED));
Here, createMainActivityBroadcast
object and assign actionRecv with delegate and usingRegisterReceiver
method, register ACTION_NEW_TABSELECTED event and associated withbroadcastRecv
Object.
Now add following code in OnBroadCastReceived method :-void OnBroadCastReceived (Context arg1, Intent arg2) { if(arg2!=null) { var txtFilmStarInfo = FindViewById<TextView> (Resource.Id.txtFilmStar); var txtFilmStarBio1 = FindViewById<TextView> (Resource.Id.txtFilmStarBio1); var txtFilmStarBio2 = FindViewById<TextView> (Resource.Id.txtFilmStarBio2); var ivFilmStar = FindViewById<ImageView> (Resource.Id.ivFilmStar); txtFilmStarInfo.Text = arg2.GetStringExtra("FilmStarName"); ivFilmStar.SetImageResource(arg2.GetIntExtra("FlimStarImageID",-1)); txtFilmStarBio1.Text = arg2.GetStringExtra("FilmStarBio1"); txtFilmStarBio2.Text = arg2.GetStringExtra("FilmStarBio2"); } }
In this method, we will find
TextView
andImageView
control and assign them values send from MainActivity. In end add following code, to register and unregister receiver OnResume and OnPause Handler. This is done to avoid receiving any event at the time it’s in pause state and also save scarce mobile resources.protected override void OnResume () { base.OnResume (); RegisterReceiver(broadcastRecv, new IntentFilter(ACTION_NEW_TABSELECTED)); } protected override void OnPause () { base.OnPause (); UnregisterReceiver(broadcastRecv); }
- Before sending message to
flimStarUI
, you have to handle Tab Button Click for tab changes. For that, you need to implement TabHost.IOnTabChangeListener in MainActivity. This put TabHost.IOnTabChangeListener.OnTabChanged method in class, which provides tabId as parameter. Add following code inOnTabChanged
method :-void TabHost.IOnTabChangeListener.OnTabChanged (string tabId) { SendMessage (tabId); } void SendMessage (string tabId) { var newTweetsIntent = new Intent (ACTION_NEW_TABSELECTED); var cItem = dictFlimStarInfo[tabId]; newTweetsIntent.PutExtra ("FilmStarName", cItem.FilmStarName); newTweetsIntent.PutExtra("FlimStarImageID", cItem.FlimStarImageID); newTweetsIntent.PutExtra("FilmStarBio1", cItem.FilmStarBio1); newTweetsIntent.PutExtra("FilmStarBio2", cItem.FilmStarBio2); SendBroadcast (newTweetsIntent); }
MainActivity class contain
dictFlimStarInfo
dictionary based on tabid as key and related information as value. So what we have done in SendMessage method is as follow- Create Intent with “NEWTABSELECTED” as action
- Add information in Intent, using PutExtra
- Using SendBroadcast dispatch intent to Activity
After all this, CallTabHost.SetOnTabChangedListener (this)
(Since MainActivity ImplementsTabHost.IOnTabChangeListener
) method to setup TabChanged listener. - Now Build and Run!
(In Potrait Mode)
Android 3.4 Inch Android 4 inch. - Running application in Landscape Mode
Android Device 7 Inch: Landscape
Putting Icon on TabButton
- Now for Adding icon on Tabbutton, add icon corresponding to actor name (First row for active and second row contain in-active state icons) :-
Amitabh Bachan Shahrukh Khan Hrithik Roshan
- Now change TabSpec.SetIndicator method to include reference to above images.
tabSpec.SetIndicator ("Hrithik",Resources.GetDrawable (Resource.Drawable.Hrithik_ico));
Resources.GetDrawable() method returns Drawable object based on Drawable Resource, which is used to display icon in Tab Button. - Build and Run, you can see Images visible at top :-
This example utilized single image for both selected and un-selected state. Now if you want different image for both, you have to use XML to define its state.
- Now to include disabled image, when Tab is unselected. We can use selector based XML to define icon for different states. read more about selector
here
<?xml version="1.0" encoding="UTF-8" ?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- When selected, use grey --> <item android:drawable="@drawable/Hrithik_ico" android:state_selected="true" /> <!-- When not selected, use white--> <item android:drawable="@drawable/Hrithik_icod" /> </selector>
Here, Hrithik_ico will be displayed, when tab is active and Hrithik_icod (disabled) will be displayed when it's inactive. Similarly make XML for rest of Actor’s.
-
Now do the following changes in code listed in step 2 :-
tabSpec.SetIndicator ("Hrithik",Resources.GetDrawable (Resource.Drawable.selectHrithik));
Now instead of icon resource use XML resource
- Build and Run :-
Points of Interest
I have used MonoAndroid for C# and Xamarin Studio to build this tutorial. Watch out, if I continue learning it, you can expect more articles coming soon.
Though Xamarin Studio is proprietary software, however they provide free starter version to built, test and publish android application. I am using same for my learning.
Articles in this series!
- MonoAndroid: Writing custom generic BaseAdapter in C#
- MonoAndroid: Using GridView in your mobile application
- MonoAndroid: Using Fragments in mobile app
- MonoAndroid: Using dotnet webservice (ASMX)
- MonoAndroid: Using Started Service
- MonoAndroid: Calling secondary activity
- MonoAndroid: Writing ExpandableListView amd BaseExpandableListAdapter in C#
- MonoAndroid: Using AlertDialog
Tips/Tricks in this Series
History
- 13-Aug-2013: First Version
- 14-Aug-2013: Added section for placing icon on Tab Buttons
- 22-Aug-2013: Added section "Other Articles in this series"
- 06-Sept-2013: Added section "Tips/Tricks in this series"
- 11-Oct-2013 : Updated article section
- 05-Nov-2013: Updated article section
- 22-Nov-2013: Updated other article section