June 19, 2016

ExpandableListView in xamarin android

Expandable List View is basically used to display the list of data with respective categories. Which has the feature to expand and collapse the items in a category when user touches category title.



Step 1: Prepare layout file.
Expandable list view requires two separate custom layout file, one for category(group) title and another one for category(child) items. One more main layout which contains ExpandableListView control.

Main.axml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#bdbdbd">
    <ExpandableListView
        android:id="@+id/lvExp"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:background="#c6c6c6"
        android:layout_margin="5dp" />
</LinearLayout>

HeaderCustomLayout.axml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="8dp"
    android:background="#fff69212">
    <TextView
        android:id="@+id/lblListHeader"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
        android:textSize="17dp"
        android:text="test"
        android:textColor="#ffffffff"
        android:textStyle="bold" />
</LinearLayout>

ListItemCustomLayout.axml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="55dip"
    android:orientation="vertical">
    <TextView
        android:id="@+id/lblListItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16dp"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:text="test"
        android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
        android:textColor="#fff69212"
        android:textStyle="bold"
        android:background="#ffffffff" />
</LinearLayout>

Step 2: Create custom adapter class for expandablelistview by inheriting from base class BaseExpandableListAdapter.
Here like listview,
GetChildView():  returns view for list child items
GetGroupView(): returns view for category title

ExpandableListAdapter.cs
using System;
using Android.Widget;
using System.Collections.Generic;
using Android.App;
using Android.Views;

namespace ExpandableListViewSample
{
 public class ExpandableListAdapter :BaseExpandableListAdapter
 {
  private Activity _context;
  private List<string> _listDataHeader; // header titles
  // child data in format of header title, child title
  private Dictionary<string, List<string>> _listDataChild;

  public ExpandableListAdapter(Activity context, List<string> listDataHeader,Dictionary<String, List<string>> listChildData) {
   _context = context;
   _listDataHeader = listDataHeader;
   _listDataChild = listChildData;
  }
        //for cchild item view
  public override Java.Lang.Object GetChild (int groupPosition, int childPosition)
  { 
   return _listDataChild[_listDataHeader[groupPosition]][childPosition];
  } 
  public override long GetChildId (int groupPosition, int childPosition)
  {
   return childPosition;
  } 

  public override View GetChildView (int groupPosition, int childPosition, bool isLastChild, View convertView,ViewGroup parent)
  { 
    string childText = (string) GetChild(groupPosition, childPosition);
   if (convertView == null) {
    convertView = _context.LayoutInflater.Inflate (Resource.Layout.ListItemCustomLayout,null); 
   }
   TextView  txtListChild = (TextView) convertView.FindViewById(Resource.Id.lblListItem);
   txtListChild.Text=childText;
   return convertView;
  } 
  public override int GetChildrenCount (int groupPosition)
  { 
   return _listDataChild [_listDataHeader [groupPosition]].Count; 
  } 
  //For header view
  public override Java.Lang.Object GetGroup (int groupPosition)
  {
   return _listDataHeader[groupPosition];
  }  
  public override int GroupCount {
   get {
    return _listDataHeader.Count;
   }
  }  
  public override long GetGroupId (int groupPosition)
  {
   return groupPosition;
  } 
  public override View GetGroupView (int groupPosition, bool isExpanded, View convertView, ViewGroup parent)
  {
   string headerTitle = (string) GetGroup(groupPosition);

   convertView = convertView ?? _context.LayoutInflater.Inflate (Resource.Layout.HeaderCustomLayout, null); 
   var lblListHeader = (TextView)convertView.FindViewById (Resource.Id.lblListHeader);  
   lblListHeader.Text=headerTitle;

   return convertView;
  } 
  public override bool HasStableIds {
   get {
    return false;
   }
  } 
  public override bool IsChildSelectable (int groupPosition, int childPosition)
  {
   return true;
  } 

  class ViewHolderItem :Java.Lang.Object
  {
  }
 }
}

Step 3: Create Activity class and do the following changes.
Prepare the data and  pass to custom adapter class. Here i used two custom list collection one for holding data of group title and another one for child items. Dictionary collection is used to combine the both.

MainActivity.cs
using Android.App;
using Android.Widget;
using Android.OS;
using System.Collections.Generic;

namespace ExpandableListViewSample
{
 [Activity (Label = "ExpandableList", MainLauncher = true, Icon = "@mipmap/icon")]
 public class MainActivity : Activity
 { 
  
  ExpandableListAdapter listAdapter;
  ExpandableListView expListView;
  List<string> listDataHeader;
  Dictionary<string, List<string>> listDataChild;
  int previousGroup = -1;

  protected override void OnCreate (Bundle savedInstanceState)
  {
   base.OnCreate (savedInstanceState);
 
   SetContentView (Resource.Layout.Main); 
   expListView = FindViewById<ExpandableListView>(Resource.Id.lvExp);

   // Prepare list data
   FnGetListData();

   //Bind list
   listAdapter = new ExpandableListAdapter(this, listDataHeader, listDataChild); 
   expListView.SetAdapter (listAdapter); 

   FnClickEvents();
  }
  void FnClickEvents()
  { 
                      //Listening to child item selection
   expListView.ChildClick+= delegate(object sender, ExpandableListView.ChildClickEventArgs e) {
    Toast.MakeText(this,"child clicked",ToastLength.Short).Show();
   };
              
    //Listening to group expand
   //modified so that on selection of one group other opened group has been closed
   expListView.GroupExpand += delegate(object sender, ExpandableListView.GroupExpandEventArgs e) {
 
    if( e.GroupPosition != previousGroup)
     expListView.CollapseGroup(previousGroup);
    previousGroup = e.GroupPosition; 
   };

    //Listening to group collapse
   expListView.GroupCollapse+= delegate(object sender, ExpandableListView.GroupCollapseEventArgs e) {
    Toast.MakeText(this,"group collapsed",ToastLength.Short).Show();
   }; 

  }
   void FnGetListData() {
   listDataHeader = new List<string>();
   listDataChild = new Dictionary<string, List<string>>();

   // Adding child data
   listDataHeader.Add("Computer science");
   listDataHeader.Add("Electrocs & comm.");
   listDataHeader.Add("Mechanical");

   // Adding child data
   var lstCS = new List<string>();
   lstCS.Add("Data structure");
   lstCS.Add("C# Programming");
   lstCS.Add("Java programming");
   lstCS.Add("ADA");
   lstCS.Add("Operation reserach");
   lstCS.Add("OOPS with C");
   lstCS.Add("C++ Programming");

   var lstEC = new List<string>();
   lstEC.Add("Field Theory");
   lstEC.Add("Logic Design");
   lstEC.Add("Analog electronics");
   lstEC.Add("Network analysis");
   lstEC.Add("Micro controller");
   lstEC.Add("Signals and system");

   var lstMech = new List<string>();
   lstMech.Add("Instrumentation technology");
   lstMech.Add("Dynamics of machinnes");
   lstMech.Add("Energy engineering");
   lstMech.Add("Design of machine");
   lstMech.Add("Turbo machine");
   lstMech.Add("Energy conversion");

   // Header, Child data
   listDataChild.Add(listDataHeader[0], lstCS);
   listDataChild.Add(listDataHeader[1], lstEC);
   listDataChild.Add(listDataHeader[2], lstMech);
  }
 }
}

Above code also shows the listening to GroupExpand, GroupCollapse and ChildClick events. Here GroupExpand is modified so that on selection of one group other opened group has been closed.

For checkout complete code available @ Git

This is all about using ExpandableListView in Xamarin android, Write back to me for any issues/suggestion and do keep visiting for much more exciting stuffs :)