Android ExpandableListView Using Kotlin With Example

In this article, we will learn about android ExpandableListView using Kotlin. We will go through various example that demonstrates how to use different attributes of ExpandableListView. We are going to learn about android ExpandableListView using kotlin in any android application. We will see how to use BaseExpandableListAdapter in ExpandableListView to show data in it. how to set groupExpandListener, groupCollapsedListener and childClickListener in expandableListView.

In this article, we will get answer to questions like –

  • What is ExpandableListView?
  • Why should we consider ExpandableListView while designing ui for any app?
  • What are possibilities using ExpandableListView while designing ui? etc.

Let’s have a quick demo of things we want to cover in this tutorial –

Output

Tutorialwing android ExpandableListView using kotlin output

ExpandableListView Tutorial output

Getting Started

We can define android ExpandableListView widget as below –

ExpandableListView is a View that shows vertically scrolling two-level list. In this view, first level groups can be expanded to show it’s children items. ExpandableListView also shows an icon beside each group to show it’s status i.e. whether it is expanded or collapsed.

Note – We can not use value wrap_content for android:layout_height attribute of ExpandableListView if parent’s size is also not strictly specified. For example, if we have used ExpandableListView inside ScrollView, we can not use wrap_content for android:layout_height because ScrollView can have any height.

Now, how do we use ExpandableListView in android application ?

Creating New Project

At first, we will create an application.
So, follow steps below to create any android project in Kotlin –

Step Description
1. Open Android Studio (Ignore if already done).
2. Go to File => New => New Project. This will open a new window. Then, under Phone and Tablet section, select Empty Activity. Then, click Next.
3. In next screen, select project name as ExpandableListView. Then, fill other required details.
4. Then, clicking on Finish button creates new project.

Newbie in Android ?

Some very important concepts (Recommended to learn before you move ahead)

Before we move ahead, we need to setup for viewBinding to access Android ExpandableListView Using Kotlin file without using findViewById() method.

Setup ViewBinding

Add viewBinding true in app/build.gradle file.

 
 android { 
 	// OTHER CODE... 
 	buildFeatures { 
 		viewBinding true 
 	} 
 } 
 

Now, set content in activity using view binding.
Open MainActivity.kt file and write below code in it.

 
 class MainActivity : AppCompatActivity() { 
 	
 	private lateinit var binding: ActivityMainBinding 
 	
 	override fun onCreate(savedInstanceState: Bundle?) { 
 		super.onCreate(savedInstanceState) 
 		binding = ActivityMainBinding.inflate(layoutInflater) 
 		val view = binding.root 
 		setContentView(view) 
 	} 
 } 
 

Now, we can access view in Kotlin file without using findViewById() method.

Using ExpandableListView in Kotlin

Follow steps below to use ExpandableListView in newly created project –

  • Open res/values/strings.xml file. Then, add below code into it.
    <resources>
        <string name="app_name">ExpandableListView</string>
    </resources>
    
  • Create View For Child Item in ExpandableListView

    Since we need an xml file that contains ui for a child item in expandableListView. So, create an xml file in main/res/layout folder with name list_item.xml.

    Now, open main/res/layout/list_item.xml file and add below code into this file.

    <?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">
    
    	<TextView
    		android:id="@+id/expandedListItem"
    		android:layout_width="match_parent"
    		android:layout_height="wrap_content"
    		android:paddingBottom="10dp"
    		android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
    		android:paddingTop="10dp"/>
    
    </LinearLayout>
    

    We are showing only name of an item in expandableListView. So, there is only textView in list_item.xml file.

  • Create View For Group Item in ExpandableListView

    Now, we need an ui for group item in expandableListView. So, create an xml file, named list_group.xml, in main/res/layout folder.

    Then, open main/res/layout/list_group.xml file and add below code into it.

    <?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">
    
    	<TextView
    		android:id="@+id/listTitle"
    		android:layout_width="match_parent"
    		android:layout_height="wrap_content"
    		android:paddingBottom="10dp"
    		android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft"
    		android:paddingTop="10dp"
    		android:textColor="@android:color/black"/>
    
    </LinearLayout>
    

    In each group item, we are showing only name of the group. So, we need only textView in xml file.

    Till now, we have defined ui for a group and child item. Now, we will use this xml file in adapter class that provides data to the expandableListView.

  • Create Adapter For ExpandableListView

    Now, we will create adapter for expandableListView that will be used to provide data to the view. So, create a kotlin file , named CustomExpandableListAdapter.kt, in main/java/com.tutorialwing.expandablelistview package.

    Now, open main/java/com.tutorialwing.expandablelistview/CustomExpandableListAdapter.kt file and add below code into it.

    package com.tutorialwing.expandablelistview
    
    import android.content.Context
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.BaseExpandableListAdapter
    import android.widget.TextView
    import com.tutorialwing.expandablelistview.databinding.ListGroupBinding
    import com.tutorialwing.expandablelistview.databinding.ListItemBinding
    
    class CustomExpandableListAdapter internal constructor(
    	private val context: Context,
    	private val titleList: List<String>,
    	private val dataList: HashMap<String, List<String>>
    ) : BaseExpandableListAdapter() {
    
    	private val inflater: LayoutInflater = LayoutInflater.from(context)
    	private lateinit var groupBinding: ListGroupBinding
    	private lateinit var itemBinding: ListItemBinding
    
    	override fun getChild(listPosition: Int, expandedListPosition: Int): Any {
    		return this.dataList[this.titleList[listPosition]]!![expandedListPosition]
    	}
    
    	override fun getChildId(listPosition: Int, expandedListPosition: Int): Long {
    		return expandedListPosition.toLong()
    	}
    
    	override fun getChildView(
    		listPosition: Int,
    		expandedListPosition: Int,
    		isLastChild: Boolean,
    		view: View?,
    		parent: ViewGroup
    	): View {
    		var convertView = view
    		val holder: ItemViewHolder
    		if (convertView == null) {
    			itemBinding = ListItemBinding.inflate(inflater)
    			convertView = itemBinding.root
    			holder = ItemViewHolder()
    			holder.label = itemBinding.expandedListItem
    			convertView.tag = holder
    		} else {
    			holder = convertView.tag as ItemViewHolder
    		}
    		val expandedListText = getChild(listPosition, expandedListPosition) as String
    		holder.label!!.text = expandedListText
    		return convertView
    	}
    
    	override fun getChildrenCount(listPosition: Int): Int {
    		return this.dataList[this.titleList[listPosition]]!!.size
    	}
    
    	override fun getGroup(listPosition: Int): Any {
    		return this.titleList[listPosition]
    	}
    
    	override fun getGroupCount(): Int {
    		return this.titleList.size
    	}
    
    	override fun getGroupId(listPosition: Int): Long {
    		return listPosition.toLong()
    	}
    
    	override fun getGroupView(
    		listPosition: Int,
    		isExpanded: Boolean,
    		view: View?,
    		parent: ViewGroup
    	): View {
    		var convertView = view
    		val holder: GroupViewHolder
    		if (convertView == null) {
    			groupBinding = ListGroupBinding.inflate(inflater)
    			convertView = groupBinding.root
    			holder = GroupViewHolder()
    			holder.label = groupBinding.listTitle
    			convertView.tag = holder
    		} else {
    			holder = convertView.tag as GroupViewHolder
    		}
    		val listTitle = getGroup(listPosition) as String
    		holder.label!!.text = listTitle
    		return convertView
    	}
    
    	override fun hasStableIds(): Boolean {
    		return false
    	}
    
    	override fun isChildSelectable(listPosition: Int, expandedListPosition: Int): Boolean {
    		return true
    	}
    
    	inner class ItemViewHolder {
    		internal var label: TextView? = null
    	}
    
    	inner class GroupViewHolder {
    		internal var label: TextView? = null
    	}
    }
    

    As we already know, this class provides data for an item to the expandableListView. We have inherited this class from BaseExpandableListAdapter class. A constructor has also been defined that accepts context, titleList and dataList. Then, we have overridden some of the methods in this class.

    They are –

    S. No. Method Description
    1. getChild() It returns the data associated with child at given child position (i.e. expandedListPosition) in a given group (i.e. listPosition) . Actual method is getChild(listPosition: Int , expandedListPosition: Int): Any .
    2. getChildId() Returns id of the child at given child position (i.e. expandedListPosition) within group at given position (i.e. listPosition) . Actual method is getChildId(listPosition: Int, expandedListPosition: Int): Long .
    3. getChildView() Returns the view for the child at given position within a group at given position. Actual method is getChildView(listPosition: Int, expandedListPosition: Int, isLastChild: Boolean, convertView: View?, parent: ViewGroup): View . In this method, define the ui for the child element. You can check how we have defined the ui for any child element in getChildView() method in CustomExpandableListAdapter class.
    4. getChildrenCount() It returns the number of children in group at given position. Actual method is getChildrenCount(listPosition: Int): Int .
    5. getGroup() Returns the data associated with group at given position (i.e. listPosition). Actual method is getGroup(listPosition: Int): Any .
    6. getGroupCount() Returns the number of groups. Actual method is getGroupCount(): Int .
    7. getGroupId() Returns the id of the group at given position (i.e. listPosition). Actual method is getGroupId(listPosition: Int): Long.
    8. getGroupView() Return the view of group at given position (i.e. listPosition). Actual method is getGroupView(listPosition: Int, isExpanded: Boolean, convertView: View?, parent: ViewGroup): View. In this method, we define the view for group. Notice that how we have used list_group.xml file for any group in CustomExpandableListAdapter class.
    9. hasStableIds() It indicates whether group or child ids are stable across changes to the underlying data.
    10. isChildSelectable() It indicates whether child at given position in given group is selectable or not. Actual method is isChildSelectable(listPosition: Int, expandedListPosition: Int): Boolean

    Since adapter class is ready now. We will use ExpandableListView widget in xml file. Then, we will access this ExpandableListView using kotlin file and perform some operations on it.

  • Open res/layout/activity_main.xml file. Then, add below code in it –
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <ExpandableListView
            android:id="@+id/expandableListView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:dividerHeight="0.5dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    In activity_main.xml file, we have used ExpandableListView widget. This widget is responsible for providing hierarchy level view. Now, we will access this ExpandableListView using kotlin file and perform some actions on it.

  • We can also access it in Kotlin File, MainActivity.kt, as below –

    package com.tutorialwing.expandablelistview
    
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.widget.ExpandableListAdapter
    import android.widget.ExpandableListView
    import android.widget.Toast
    import com.tutorialwing.expandablelistview.databinding.ActivityMainBinding
    
    class MainActivity : AppCompatActivity() {
    
    	private lateinit var binding: ActivityMainBinding
    
    	private var adapter: ExpandableListAdapter? = null
    	private var titleList: List<String>? = null
    
    	val data: HashMap<String, List<String>>
    		get() {
    			val listData = HashMap<String, List<String>>()
    
    			val redmiMobiles = ArrayList<String>()
    			redmiMobiles.add("Redmi Y2")
    			redmiMobiles.add("Redmi S2")
    			redmiMobiles.add("Redmi Note 5 Pro")
    			redmiMobiles.add("Redmi Note 5")
    			redmiMobiles.add("Redmi 5 Plus")
    			redmiMobiles.add("Redmi Y1")
    			redmiMobiles.add("Redmi 3S Plus")
    
    			val micromaxMobiles = ArrayList<String>()
    			micromaxMobiles.add("Micromax Bharat Go")
    			micromaxMobiles.add("Micromax Bharat 5 Pro")
    			micromaxMobiles.add("Micromax Bharat 5")
    			micromaxMobiles.add("Micromax Canvas 1")
    			micromaxMobiles.add("Micromax Dual 5")
    
    			val appleMobiles = ArrayList<String>()
    			appleMobiles.add("iPhone 8")
    			appleMobiles.add("iPhone 8 Plus")
    			appleMobiles.add("iPhone X")
    			appleMobiles.add("iPhone 7 Plus")
    			appleMobiles.add("iPhone 7")
    			appleMobiles.add("iPhone 6 Plus")
    
    			val samsungMobiles = ArrayList<String>()
    			samsungMobiles.add("Samsung Galaxy S9+")
    			samsungMobiles.add("Samsung Galaxy Note 7")
    			samsungMobiles.add("Samsung Galaxy Note 5 Dual")
    			samsungMobiles.add("Samsung Galaxy S8")
    			samsungMobiles.add("Samsung Galaxy A8")
    			samsungMobiles.add("Samsung Galaxy Note 4")
    
    			listData["Redmi"] = redmiMobiles
    			listData["Micromax"] = micromaxMobiles
    			listData["Apple"] = appleMobiles
    			listData["Samsung"] = samsungMobiles
    
    			return listData
    		}
    
    	override fun onCreate(savedInstanceState: Bundle?) {
    		super.onCreate(savedInstanceState)
    
    		binding = ActivityMainBinding.inflate(layoutInflater)
    		setContentView(binding.root)
    
    		setupExpandableListView()
    	}
    
    	private fun setupExpandableListView() {
    		val expandableListView = binding.expandableListView
    		val listData = data
    		titleList = ArrayList(listData.keys)
    		adapter = CustomExpandableListAdapter(this, titleList as ArrayList<String>, listData)
    		expandableListView.setAdapter(adapter)
    
    		expandableListView.setOnGroupExpandListener { groupPosition ->
    			Toast.makeText(
    				applicationContext,
    				(titleList as ArrayList<String>)[groupPosition] + " List Expanded.",
    				Toast.LENGTH_SHORT
    			).show()
    		}
    
    		expandableListView.setOnGroupCollapseListener { groupPosition ->
    			Toast.makeText(
    				applicationContext,
    				(titleList as ArrayList<String>)[groupPosition] + " List Collapsed.",
    				Toast.LENGTH_SHORT
    			).show()
    		}
    
    		expandableListView.setOnChildClickListener { parent, v, groupPosition, childPosition, id ->
    			Toast.makeText(
    				applicationContext,
    				"Clicked: " + (titleList as ArrayList<String>)[groupPosition] + " -> " + listData[(titleList as ArrayList<String>)[groupPosition]]!!.get(
    					childPosition
    				),
    				Toast.LENGTH_SHORT
    			).show()
    			false
    		}
    	}
    }
    

    Here, we have accessed expandableListView using kotlin file i.e. In MainActivity.kt file. Then, an instance of CustomExpandableListAdapter class has been created. Then, this adapter class is set as an adapter of expandableListView.

    After that we have set some listeners (GroupExpandListener, GroupCollapseListener and ChildClickListener) to expandableListView.

Now, run the application. We will get output as below –

Tutorialwing android ExpandableListView using kotlin output

ExpandableListView Tutorial output

8. Setting Listeners in expandableListView

You can set different listeners in expandableListView –

  1. Setting GroupExpandListener in ExpandableListView

    We need group expand listener to perform the task whenever any group is expanded. We can set group expand listener in expandableListView using kotin as below –

    expandableListView!!.setOnGroupExpandListener { 
       groupPosition -> Toast.makeText(applicationContext, (titleList as ArrayList<String>)[groupPosition] + " List Expanded.", Toast.LENGTH_SHORT).show() 
    }
    

    Here, we are showing a toast message whenever any group is expanded and this method is called.

    We can also perform the same task by overriding onGroupExpanded(groupPosition: Int) method in adapter class of ExpandableListView. For example, in given CustomExpandableListAdapter.kt class, we can override onGroupExpanded(groupPosition: Int) method as below –

    override fun onGroupExpanded(groupPosition: Int) {
       Toast.makeText(context, (titleList as ArrayList<String>)[groupPosition] + " List Expanded.", Toast.LENGTH_SHORT).show()
    }
    

    Here, we are also showing a toast message whenever this method is called.

  2. Setting GroupCollapsedListener in ExpandableListView

    We need groupCollapsed listener to perform some operations whenever any group is collapsed. You can set group collapsed listener in expandableListView using kotlin as shown below –

    expandableListView!!.setOnGroupCollapseListener { groupPosition ->
         Toast.makeText(applicationContext, (titleList as ArrayList<String>)[groupPosition] + " List Collapsed.", Toast.LENGTH_SHORT).show()
    }
    

    Or, you can perform same task by overriding onGroupCollapsed(groupPosition: Int) method in adapter class of expandableListView. For example, in given CustomExpandableListAdapter.kt class, you can override onGroupCollapsed(groupPosition: Int) method as below –

    override fun onGroupCollapsed(groupPosition: Int) {
       Toast.makeText(context, (titleList as ArrayList<String>)[groupPosition] + " List Collapsed.", Toast.LENGTH_SHORT).show()
    }
    
  3. Setting Child Click Listener in ExpandableListView

    We need child click listener in expandableListView to perform some operations when any child item is clicked. We can set child click listener in expandableListView using kotlin as shown below –

    expandableListView!!.setOnChildClickListener { 
        parent, v, groupPosition, childPosition, id -> 
        Toast.makeText(applicationContext, "Clicked: " + (titleList as ArrayList<String>)[groupPosition] + " -> " + listData[(titleList as ArrayList<String>)[groupPosition]]!!.get(childPosition), Toast.LENGTH_SHORT).show()
        false
    }
    

    Here, we are also showing toast message whenever any child item is clicked.

Different Attributes of ExpandableListView in XML

Now, we will see how to use different attributes of Android ExpandableListView using Kotlin to customise it –

Set Id of ExpandableListView

Many a time, we need id of View to access it in kotlin file or create ui relative to that view in xml file. So, we can set id of ExpandableListView using android:id attribute like below –

    <ExpandableListView
        android:id="@+id/expandableListView_ID"
        />

Here, we have set id of ExpandableListView as expandableListView_ID using android:id=”” attribute. So, if we need to reference this ExpandableListView, we need to use this id – expandableListView_ID.
Learn to Set ID of ExpandableListView Dynamically

Set Width of ExpandableListView

We use android:layout_width=”” attribute to set width of ExpandableListView.
We can do it as below –

    <ExpandableListView
        android:id="@+id/expandableListView_ID"
        android:layout_width="wrap_content"
        />

Width can be either “MATCH_PARENT” or “WRAP_CONTENT” or any fixed value (like 20dp, 30dp etc.).
Learn to Set Width of ExpandableListView Dynamically

Set Height of ExpandableListView

We use android:layout_height=”” attribute to set height of ExpandableListView.
We can do it as below –

    <ExpandableListView
        android:id="@+id/expandableListView_ID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

Height can be either “MATCH_PARENT” or “WRAP_CONTENT” or any fixed value.
Learn to Set Height of ExpandableListView Dynamically

Set Padding of ExpandableListView

We use android:padding=”” attribute to set padding of ExpandableListView.
We can do it as below –

    <ExpandableListView
        android:id="@+id/expandableListView_ID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        />

Here, we have set padding of 10dp in ExpandableListView using android:padding=”” attribute.
Learn to Set Padding of ExpandableListView Dynamically

Set Margin of ExpandableListView

We use android:layout_margin=”” attribute to set margin of ExpandableListView.
We can do it as below –

    <ExpandableListView
        android:id="@+id/expandableListView_ID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        />

Here, we have set margin of 10dp in ExpandableListView using android:layout_margin=”” attribute.
Learn to Set Margin of ExpandableListView Dynamically

Set Background of ExpandableListView

We use android:background=”” attribute to set background of ExpandableListView.
We can do it as below –

    <ExpandableListView
        android:id="@+id/expandableListView_ID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff0000"
        />

Here, we have set background of color #ff0000 in ExpandableListView using android:background=”” attribute.
Learn to Set Background of ExpandableListView Dynamically

Set Visibility of ExpandableListView

We use android:visibility=”” attribute to set visibility of ExpandableListView.
We can do it as below –

    <ExpandableListView
        android:id="@+id/expandableListView_ID"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        />

Here, we have set visibility of ExpandableListView using android:visiblity=”” attribute. Visibility can be of three types – gone, visible and invisible
Learn to Set Visibility of ExpandableListView Dynamically

Thus, we have seen what is ExpandableListView, how can we use android ExpandableListView using Kotlin ? etc. We also went through different attributes of android ExpandableListView.

Leave a Reply