Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

MonoAndroid: Writing ExpandableListView amd BaseExpandableListAdapter in C#

4.81/5 (15 votes)
21 Nov 2013CPOL4 min read 47.9K   1K  
Creating ExpandableListView and populating it with BaseExpandableListAdapter derived class

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.

Image 1

Step By Step we move forward

  1. Create Android application by selecting New ->Solutions and provide its name “ExpendListBox”

    Image 2


  2.  
  3. 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:-
    XML
        <?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"
        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.


  4. Add another layout file ListControl_Child.axml,This layout file will act as view for Child Row.Add following code to it:-
    XML
        <?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"
        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.



  5. Now, add new C# file and name it expendListAdapter.cs, and  Implement BaseExpandableListAdapter, the bare skeleton class look like this :-  
    C#
    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
      C#
      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
      C#
      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



  6. Time is now to add GetGroupView and GetChildView inside ExpendListAdapter class, here is code for GetGroupView:-
    C#
    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:-

    C#
    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;
    }

  7. In Main.axml file, add ExpandableListView control. After that axml code looks like this :-
    XML
    <?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">   
        <ExpandableListView
            android:minWidth="25px"
            android:minHeight="25px"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:id="@+id/ctlExListBox" />
    </LinearLayout>

  8. In MainActivity.cs file, Add method by name CreateExpendableListData, which will create temporary data for posting it in ListView, code is as follow:- 
    C#
    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>



  9. 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.
    C#
    var ctlExListBox = FindViewById<ExpandableListView> (Resource.Id.ctlExListBox);
    	ctlExListBox.SetAdapter (new ExpendListAdapter (this, dictGroup));

    Following diagram explain working of BaseExpandableListAdapter

    Image 3
  10. Now add ctlExListBox.ChildClick handler and in handler code add Toast to generate message of clicked user
    C#
    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

  11. Now Build and Run!

     Image 4 

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)