
# Expandable RecyclerView [](https://circleci.com/gh/thoughtbot/expandable-recycler-view/tree/master)
Custom RecyclerViewAdapters for expanding and collapsing groups with support for multiple view types
<img src="https://cloud.githubusercontent.com/assets/5386934/17074123/b9d1efca-502c-11e6-9c9f-fb6180ee337f.gif" width=300 />
## Download
ExpandableRecyclerView:
```groovy
compile 'com.thoughtbot:expandablerecyclerview:1.4'
```
ExpandableCheckRecyclerView:
```groovy
compile 'com.thoughtbot:expandablecheckrecyclerview:1.4'
```
## Usage
Let's say you are a rock star :guitar: and you want to build an app to show a list of your favorite `Genre`s with a list of their top `Artist`s.
First, define your custom `ExpandableGroup` class:
``` java
public class Genre extends ExpandableGroup<artist rel='nofollow' onclick='return false;'> {
public Genre(String title, List<artist rel='nofollow' onclick='return false;'> items) {
super(title, items);
}
}
```
Next up, let's create the `ChildViewHolder` and `GroupViewHolder`. These are both wrappers around regular ol' `RecyclerView.ViewHolder`s so implement any view inflation and binding methods you may need.
``` java
public class GenreViewHolder extends GroupViewHolder {
private TextView genreTitle;
public GenreViewHolder(View itemView) {
super(itemView);
genreTitle = itemView.findViewById(R.id.genre_title);
}
public void setGenreTitle(ExpandableGroup group) {
genreTitle.setText(group.getTitle());
}
}
```
``` java
public class ArtistViewHolder extends ChildViewHolder {
private TextView artistName;
public ArtistViewHolder(View itemView) {
super(itemView);
artistName = itemView.findViewById(R.id.artist_name);
}
public void setArtistName(Artist artist) {
artistName.setText(artist.getTitle());
}
}
```
Now we are ready for the juicy part - let's make our `ExpandableRecyclerViewAdapter`
By including your `GroupViewHolder` and `ChildViewHolder` in the definition of the class, you'll see that the `onCreateGroupViewHolder` and `onCreateChildViewHolder` methods return the correct type :thumbsup:
``` java
public class GenreAdapter extends ExpandableRecyclerViewAdapter<GenreViewHolder, ArtistViewHolder> {
public GenreAdapter(List<? extends ExpandableGroup> groups) {
super(groups);
}
@Override
public GenreViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.list_item_genre, parent, false);
return new GenreViewHolder(view);
}
@Override
public ArtistViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.list_item_artist, parent, false);
return new ArtistViewHolder(view);
}
@Override
public void onBindChildViewHolder(ArtistViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {
final Artist artist = ((Genre) group).getItems().get(childIndex);
holder.setArtistName(artist.getName());
}
@Override
public void onBindGroupViewHolder(GenreViewHolder holder, int flatPosition, ExpandableGroup group) {
holder.setGenreTitle(group);
}
}
```
Lastly you'll need either an `Activity` or `Fragment` to host your adapter. Once you've got that up and running, all that's left is to instantiate your fancy new `GenreAdapter` with a `List<Genre>`
``` java
public class GenreActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
List<Genre> genres = getGenres(); //see sample project's GenreDataFactory.java class for getGenres() method
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//instantiate your adapter with the list of genres
GenreAdapter adapter = new GenreAdapter(genres);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
...
}
}
```
## Saving And Restoring Expand / Collapse State
If you want to save the expand and collapse state of your adapter, you have to explicitly call through to the adapters `onSaveInstanceState()` and `onRestoreInstanceState()`in the calling `Activity`
```java
public class GenreActivity extends Activity {
...
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
adapter.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
adapter.onRestoreInstanceState(savedInstanceState);
}
}
```
## Programmatic Expanding and Collapsing
The `ExpandableRecyclerViewAdapter` exposes methods to control the expanded and
collapsed state.
First up we have the toggles, `.toggleGroup(int)` and
`.toggleGroup(ExpandableGroup)`. These are handy for when you control the
states explicitly.
```java
public class GenreActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Button showAllToggle = findViewById(R.id.show_all);
showAllToggle.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
for (int i = adapter.groups().size() - 1; i >= 0; i--) {
adapter.toggleGroup(i);
}
}
});
}
}
```
We also expose explicit methods to control the expanding and collapsing of
specific groups, `.expandGroup()` and `.collapseGroup()`. For example, to
expand the first group immediately:
```java
public class GenreActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
adapter.expandGroup(0);
}
}
```
## Adding Custom Expand / Collapse Animations
If you want to add a custom `Drawable` that animates based on a groups state, override the `expand()` and `collapse()` methods in your `GroupViewHolder`:
``` java
public class GenreViewHolder extends GroupViewHolder {
...
@Override
public void expand() {
animateExpand();
}
@Override
public void collapse() {
animateCollapse();
}
}
```
## Listening to Expand/Collapse events
If you want register an `ExpandCollapseListener` outside of the adapter, you can simply call `setOnGroupExpandCollapseListener` on the `ExpandableRecyclerViewAdapter`
``` java
adapter.setOnGroupExpandCollapseListener(new GroupExpandCollapseListener() {
@Override
public void onGroupExpanded(ExpandableGroup group) {
}
@Override
public void onGroupCollapsed(ExpandableGroup group) {
}
});
```
## Multiple Child and Group Types
The `MultiTypeExpandableRecyclerViewAdapter` allows subclasses to implement multiple different view types for both children and groups.
Continuing with our genre example, let's say you wanted to display regular artists differently from your favorite artists. Let's start by making a new `FavoriteArtistViewHolder`
``` java
public class FavoriteArtistViewHolder extends ChildViewHolder {
private TextView favoriteArtistName;
public FavoriteArtistViewHolder(View itemView) {
super(itemView);
favoriteArtistName = (TextView) itemView.findViewById(R.id.list_item_favorite_artist_name);
}
public void setArtistName(String name) {
favoriteArtistName.setText(name);
}
```
Just like the regular `ArtistViewHolder`, `FavoriteArtistViewHolder` must extends `ChildViewHolder`.
Next up, let's create a subclass of `MultiTypeExpandableRecyclerViewAdapter` called `MultiTypeGenreAdapter` and let's add two static `int`s representing our two artist view types:
```java
public class MultiTypeGenreAdapter extends MultiTypeExpandableRecyclerViewAdapter<GenreViewHolder, ChildViewHolder> {
public static final int FAVORITE_VIEW_TYPE = 3;
public static final int ARTIST_VIEW_TYPE = 4;
...
```
Notice we started u