Introduction
For past few months, I am learning mobile programming in android using MonoAndroid
SDK. I have published few articles here, whose references are at the bottom. People ask why I am coding in C#
when android natively support language whose syntax are closer to JAVA
then C#
. My simple reply is that I am working in VC++ and C# for now almost 9+ years, learning curve for me learning mobile programming in language is far much better then programming same in java. Everybody has his/her viewpoint, I respect those
views.
In this article, I am going to demonstrate creation of custom BaseExpandableListAdapter, which is used by ExpandableListView to create parent child type of view. Generally they are referred in android world as Group and their child items.
Step By Step we move forward
- Create Android application by selecting New ->Solutions and provide its name “ExpendListBox”
- Once Project is created, Open Resource Folder->Layout and Add new
layout file,
ListControl_Group.axml,This layout file will act as view for Group Row.Add
following code to it:-
="1.0"="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"
android:minWidth="25px"
android:minHeight="25px"
android:background="#ffdf872c">
<TextView
android:text="Medium Text"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/txtLarge"
android:layout_marginLeft="42.0dp"
android:layout_marginRight="10dp"
android:background="#ffdf872c"
android:layout_marginTop="10.0dp"
android:layout_marginBottom="6.7dp"
android:textColor="#ffbd1c1c"
android:shadowColor="#fff9ea14"
android:gravity="center_vertical" />
</LinearLayout>
In the blank layout, I have added Medium Text View and changed the background to dark orange, (I am lying, some random color which resemble closer to orange), apart tweaking margin.
- Add another layout file
ListControl_Child.axml,This layout file will act as view for Child Row.Add
following code to it:-
="1.0"="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"
android:id="@+id/linearLayout1"
android:minWidth="25px"
android:minHeight="25px">
<TextView
android:text="Small Text"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/txtSmall"
android:layout_marginLeft="26.0dp" />
</LinearLayout>
In the blank layout, I have added Small Text View, apart tweaking margin.
- Now, add new C# file and name
it expendListAdapter.cs, and Implement
BaseExpandableListAdapter
, the bare skeleton class look like this :-
public class ExpendListAdapter: BaseExpandableListAdapter
{
#region implemented abstract members of BaseExpandableListAdapter
public override Java.Lang.Object GetChild (int groupPosition, int childPosition)
{
throw new NotImplementedException ();
}
public override long GetChildId (int groupPosition, int childPosition)
{
throw new NotImplementedException ();
}
public override int GetChildrenCount (int groupPosition)
{
throw new NotImplementedException ();
}
public override View GetChildView (int groupPosition, int childPosition, bool isLastChild, View convertView, ViewGroup parent)
{
throw new NotImplementedException ();
}
public override Java.Lang.Object GetGroup (int groupPosition)
{
throw new NotImplementedException ();
}
public override long GetGroupId (int groupPosition)
{
throw new NotImplementedException ();
}
public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent)
{
throw new NotImplementedException ();
}
public override bool IsChildSelectable (int groupPosition, int childPosition)
{
throw new NotImplementedException ();
}
public override int GroupCount {
get {
throw new NotImplementedException ();
}
}
public override bool HasStableIds {
get {
throw new NotImplementedException ();
}
}
#endregion
}
}
- Now Add three private member of type
Dictionary<string, List<string>>
,List<string>
and Activity
- Add parameterized constructor, which take
Activity
and Dictionary<string, List<string>>
as argument, and assign these values to private member listed in first step. - For
List<string>
, give values of all the keys in dictionary - Coding child item method like GetChild, GetChildId, GetChildrenCount and IsChildSelectable is fairly simple as we are just returning appropriate item from dictionary, please review code below
Dictionary<string, List<string>> _dictGroup =null;
List<string> _lstGroupID = null;
Activity _activity;
public ExpendListAdapter (Activity activity,
Dictionary<string, List<string>> dictGroup)
{
_dictGroup = dictGroup;
_activity = activity;
_lstGroupID = dictGroup.Keys.ToList();
}
public override Java.Lang.Object GetChild (int groupPosition, int childPosition)
{
return _dictGroup [_lstGroupID [groupPosition]] [childPosition];
}
public override long GetChildId (int groupPosition, int childPosition)
{
return childPosition;
}
public override int GetChildrenCount (int groupPosition)
{
return _dictGroup [_lstGroupID [groupPosition]].Count;
}
public override bool IsChildSelectable (int groupPosition, int childPosition)
{
return true;
}
- Similarly group item method like GetGroup, GetGroupId and GroupCount are fairly simple as we are just returning appropriate item from dictionary, please review code below
public override Java.Lang.Object GetGroup (int groupPosition)
{
return _lstGroupID [groupPosition];
}
public override long GetGroupId (int groupPosition)
{
return groupPosition;
}
public override int GroupCount {
get {
return _dictGroup.Count;
}
}
GetChildView and GetGroupView are two methods which will take care of inflating and populating of the view, will explain that in next step
- Time is now to add GetGroupView and GetChildView inside
ExpendListAdapter
class, here is code for GetGroupView:-
public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent)
{
var item = _lstGroupID [groupPosition];
if (convertView == null)
convertView = _activity.LayoutInflater.Inflate (Resource.Layout.ListControl_Group, null);
var textBox = convertView.FindViewById<TextView> (Resource.Id.txtLarge);
textBox.SetText (item, TextView.BufferType.Normal);
return convertView;
}
Here, I check whether convertView has memory. In case it doesn’t, Inflate ListControl_Group
layout. Once inflated, try to find out textview and update it with Group header.
For GetChildView, coding is similar to GetGroupView except of inflating ListControl_Group, inflate ListControl_Child. Code is as follow:-
public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent)
{
var item = _dictGroup [_lstGroupID [groupPosition]] [childPosition];
if (convertView == null)
convertView = _activity.LayoutInflater.Inflate (Resource.Layout.ListControl_ChildItem, null);
var textBox = convertView.FindViewById<TextView> (Resource.Id.txtSmall);
textBox.SetText (item, TextView.BufferType.Normal);
return convertView;
}
- In Main.axml file, add
ExpandableListView
control. After that axml code looks like this :-
="1.0"="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">
<ExpandableListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/ctlExListBox" />
</LinearLayout>
- In MainActivity.cs file, Add method by name CreateExpendableListData, which will create temporary data for posting it in ListView, code is as follow:-
void CreateExpendableListData ()
{
for (int iGroup = 1; iGroup <= 3; iGroup++) {
var lstChild = new List<string> ();
for (int iChild = 1; iChild <= 3; iChild++) {
lstChild.Add (string.Format ("Group {0} Child {1}", iGroup, iChild));
}
dictGroup.Add (string.Format ("Group {0}", iGroup), lstChild);
}
lstKeys = new List<string>(dictGroup.Keys);
}
This method will add three groups and each have three children each. dictGroup
local field of type Dictionary<string, List<string>> and
lstKeys
is of type List<string>
- Now you have find ExpandableListView using FindViewById<ExpandableListView> using id “Resource.Id.ctlExListBox” and create our customListAdapter by passing current activity and
dictionary we created in last step and assign it to ExpandableListView Object adapter property.
var ctlExListBox = FindViewById<ExpandableListView> (Resource.Id.ctlExListBox);
ctlExListBox.SetAdapter (new ExpendListAdapter (this, dictGroup));
Following diagram explain working of BaseExpandableListAdapter
- Now add
ctlExListBox.ChildClick
handler and in handler code add Toast to generate message of clicked user
ctlExListBox.ChildClick += delegate(object sender, ExpandableListView.ChildClickEventArgs e) {
var itmGroup = lstKeys [e.GroupPosition];
var itmChild = dictGroup [itmGroup] [e.ChildPosition];
Toast.MakeText (this, string.Format ("You Click on Group {0} with child {1}", itmGroup, itmChild),
ToastLength.Long).Show ();
};
You can read more about Toast
class here
- Now 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
and creating step by step tutorials here.
Articles in this series!
Tips/Tricks in this Series
History
- 01-Nov-2013: First Version
- 22-Nov-2013: Updated other article section