The Expandable RecyclerView library is a lightweight library that simplifies the inclusion of expandable dropdown rows in your RecyclerView. In it, you have two types of views. The parent view is the one visible by default, and it contains the children. The child view is the one that is expanded or collapsed, and will appear when a parent item is clicked.

In this post, we will implement the Expandable RecyclerView in the CriminalIntent application from our Android programming guide. We’ll be showing a more detailed view of each crime from the main list fragment.

All expanding and collapsing functionality is handled in the adapter, meaning that your RecyclerView is just a stock RecyclerView. All you’ll need to do to set up the Expandable RecyclerView in a layout is add a stock RecyclerView to your activity or fragment’s XML layout.

1. The ViewHolders

First, let’s create the XML layouts for our parent and child views. Our parent layout is going to include the title of the crime and a dropdown arrow to display an animation when the item is expanded or collapsed. We’ll call this layout list_item_crime_parent.

Now, onto the child layout. The child is going to contain a TextView that shows the date of the crime and a checkbox that we can click when the crime is solved. We’ll call this layout list_item_crime_child.

Now that we have both of our layouts ready, let’s set up each ViewHolder. We will create a class called CrimeParentViewHolder that extends ParentViewHolder and another class called CrimeChildViewHolder that extends ChildViewHolder.

Let’s start with CrimeParentViewHolder. Go ahead and create a public TextView and ImageButton variable for our two views, like so:

2. Parents and Their Children

For our CriminalIntent example, the data that we want to display in our list items are fields of our Crime object. Since we have both a parent and a child layout, the best practice is to create a child object, separate from the parent, to hold the data that will be displayed in the child.

In this case, our parent object will be the Crime object itself. Let’s make our Crime object implement ParentObject. Implement the getter and setter methods with a list variable:

publicclassCrimeimplementsParentObject{/* Create an instance variable for your list of children */privateList<Object>mChildrenList;/**
* Your constructor and any other accessor
* methods should go here.
*/@OverridepublicList<Object>getChildObjectList(){returnmChildrenList;}@OverridepublicvoidsetChildObjectList(List<Object>list){mChildrenList=list;}}

The expandable RecyclerView allows for multiple children or none, so the children need to be added as a list. You can either add the children directly in the getChildObjectList method we implemented by returning your List of children, or you can add them when we create the list of items by calling setChildObjectList with the list of respective children. I will be doing the latter in this demo.

Our child object will hold two values, a String for the date and a boolean for the solved flag:

We’ll populate each parent object with children in the final step. Now, let’s get the adapter working!

3. The Adapter

We’re going need to implement a custom adapter that extends ExpandableRecyclerAdapter. Inside our adapter, we will implement a few methods so we can populate the data in our ViewHolders.

First, let’s create a class and call it CrimeExpandableAdapter. Make it extend ExpandableRecyclerAdapter and give it our two ViewHolder type parameters in the order of <parent view holder, child view holder>. Our header will look like this:

You’ll need to implement the four inherited methods, onCreateParentViewHolder, onBindParentViewHolder, onCreateChildViewHolder and onBindChildViewHolder. Go ahead and implement the default constructor that will take in a Context and a List of ParentObjects.

In our constructor, let’s get access to the LayoutInflater and call it mInflater. This will be used in our onCreateParentViewHolder and onCreateChildViewHolder to inflate the layouts we created earlier. In our constructor, add mInflater = LayoutInflater.from(context);. This will create a layout inflater for us.

Now, let’s create our views and add them to the custom ViewHolders we made earlier. In onCreateParentViewHolder, let’s replace the return null line with these two lines:

To finish off our adapter, let’s work on our onBindViewHolder methods, starting with onBindParentViewHolder. In onBindParentViewHolder, three variables are passed in: the viewholder we created in onCreateParentViewHolder, the position of the item and the parent object associated with that position. We’re going to need to cast the passed parent object to our parent object type (which, as you recall, is our Crime object). Let’s make our onBindParentViewHolder look like this:

Finally, we will set up our CrimeChildViewHolder. Recall that our CrimeChildViewHolder contains a TextView for a date and a CheckBox to indicate whether the crime is solved. Our onBindChildViewHolder will look like this:

Another option is to create a new list of children directly in your Crime object and set that to be the childrenList. A third option would be to create a list of children in getChildObjectList and return that list.

Note that the list you pass into your adapter must be of the type ParentObject.

The setCustomParentAnimationView allows for the arrow in the parent layout to rotate on expand/collapse and setParentClickableAnimationDefaultDuration gives it a default rotation time of 200 milliseconds. setParentAndIconExpandOnClick ensures that we can click both the parent and the arrow to expand the item. You can always remove these if you don’t want an animation or custom triggering view.

Finally, set the RecyclerView’s adapter to finish it up:

mCrimeRecyclerView.setAdapter(mCrimeExpandableAdapter);

That’s it! You now should have a RecyclerView that expands and collapses and has a nice rotation animation to go along with it.

Want to improve it or see more?

The library is open source, so visit the project’s GitHub page to see all the source code, then send a pull request if there are new features you’d like to add.

*Ryan Brooks was an Android intern this summer. Apply now to join our team as an intern in Fall 2015.