{"id":19593160,"url":"https://github.com/thoughtbot/expandable-recycler-view","last_synced_at":"2025-04-27T14:34:19.515Z","repository":{"id":52601292,"uuid":"63989593","full_name":"thoughtbot/expandable-recycler-view","owner":"thoughtbot","description":"Custom Android RecyclerViewAdapters that collapse and expand","archived":true,"fork":false,"pushed_at":"2024-09-20T17:33:11.000Z","size":209,"stargazers_count":2121,"open_issues_count":0,"forks_count":416,"subscribers_count":36,"default_branch":"main","last_synced_at":"2025-03-14T15:49:18.640Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://robots.thoughtbot.com/introducing-expandablerecyclerview","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thoughtbot.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-07-22T23:32:30.000Z","updated_at":"2025-03-04T12:58:09.000Z","dependencies_parsed_at":"2022-09-10T23:41:24.037Z","dependency_job_id":null,"html_url":"https://github.com/thoughtbot/expandable-recycler-view","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thoughtbot%2Fexpandable-recycler-view","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thoughtbot%2Fexpandable-recycler-view/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thoughtbot%2Fexpandable-recycler-view/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thoughtbot%2Fexpandable-recycler-view/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thoughtbot","download_url":"https://codeload.github.com/thoughtbot/expandable-recycler-view/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251155042,"owners_count":21544588,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-11T08:38:24.186Z","updated_at":"2025-04-27T14:34:18.919Z","avatar_url":"https://github.com/thoughtbot.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Deprecated as of September 20, 2024\n\nexpandable-recycler-view is no longer maintained. If you wish to continue to develop this code yourself, we recommend you fork it.\n\n![logo](https://images.thoughtbot.com/blog-vellum-image-uploads/lpMtQDMlRindAIJOHlGl_expandable-recycler-view-logo.png)\n\n# Expandable RecyclerView [![CircleCI](https://circleci.com/gh/thoughtbot/expandable-recycler-view/tree/master.svg?style=svg)](https://circleci.com/gh/thoughtbot/expandable-recycler-view/tree/master)\nCustom RecyclerViewAdapters for expanding and collapsing groups with support for multiple view types\n\n\u003cimg src=\"https://cloud.githubusercontent.com/assets/5386934/17074123/b9d1efca-502c-11e6-9c9f-fb6180ee337f.gif\" width=300 /\u003e\n\n## Download\nExpandableRecyclerView:\n```groovy\ncompile 'com.thoughtbot:expandablerecyclerview:1.4'\n```\n\nExpandableCheckRecyclerView:\n```groovy\ncompile 'com.thoughtbot:expandablecheckrecyclerview:1.4'\n```\n\n## Usage\nLet'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.\n\nFirst, define your custom `ExpandableGroup` class:\n\n``` java\npublic class Genre extends ExpandableGroup\u003cArtist\u003e {\n\n  public Genre(String title, List\u003cArtist\u003e items) {\n    super(title, items);\n  }\n}\n```\n\nNext 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.\n\n``` java\npublic class GenreViewHolder extends GroupViewHolder {\n\n  private TextView genreTitle;\n\n  public GenreViewHolder(View itemView) {\n    super(itemView);\n    genreTitle = itemView.findViewById(R.id.genre_title);\n  }\n\n  public void setGenreTitle(ExpandableGroup group) {\n    genreTitle.setText(group.getTitle());\n  }\n}\n```\n\n``` java\npublic class ArtistViewHolder extends ChildViewHolder {\n\n  private TextView artistName;\n\n  public ArtistViewHolder(View itemView) {\n    super(itemView);\n    artistName = itemView.findViewById(R.id.artist_name);\n  }\n\n  public void setArtistName(Artist artist) {\n    artistName.setText(artist.getTitle());\n  }\n}\n```\n\nNow we are ready for the juicy part - let's make our `ExpandableRecyclerViewAdapter`\n\nBy 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:\n\n``` java\npublic class GenreAdapter extends ExpandableRecyclerViewAdapter\u003cGenreViewHolder, ArtistViewHolder\u003e {\n\n  public GenreAdapter(List\u003c? extends ExpandableGroup\u003e groups) {\n    super(groups);\n  }\n\n  @Override\n  public GenreViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {\n    View view = inflater.inflate(R.layout.list_item_genre, parent, false);\n    return new GenreViewHolder(view);\n  }\n\n  @Override\n  public ArtistViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {\n    View view = inflater.inflate(R.layout.list_item_artist, parent, false);\n    return new ArtistViewHolder(view);\n  }\n\n  @Override\n  public void onBindChildViewHolder(ArtistViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {\n    final Artist artist = ((Genre) group).getItems().get(childIndex);\n    holder.setArtistName(artist.getName());\n  }\n\n  @Override\n  public void onBindGroupViewHolder(GenreViewHolder holder, int flatPosition, ExpandableGroup group) {\n    holder.setGenreTitle(group);\n  }\n}\n```\n\nLastly 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\u003cGenre\u003e`\n\n``` java\npublic class GenreActivity extends Activity {\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n\n    ...\n\n    List\u003cGenre\u003e genres = getGenres(); //see sample project's GenreDataFactory.java class for getGenres() method\n    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);\n    LinearLayoutManager layoutManager = new LinearLayoutManager(this);\n\n    //instantiate your adapter with the list of genres\n    GenreAdapter adapter = new GenreAdapter(genres);\n    recyclerView.setLayoutManager(layoutManager);\n    recyclerView.setAdapter(adapter);\n\n    ...\n\n  }\n}\n```\n\n## Saving And Restoring Expand / Collapse State\n\nIf 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`\n\n```java\npublic class GenreActivity extends Activity {\n\n  ...\n\n  @Override\n  protected void onSaveInstanceState(Bundle outState) {\n    super.onSaveInstanceState(outState);\n    adapter.onSaveInstanceState(outState);\n  }\n\n  @Override\n  protected void onRestoreInstanceState(Bundle savedInstanceState) {\n    super.onRestoreInstanceState(savedInstanceState);\n    adapter.onRestoreInstanceState(savedInstanceState);\n  }\n}\n\n```\n\n## Programmatic Expanding and Collapsing\n\nThe `ExpandableRecyclerViewAdapter` exposes methods to control the expanded and\ncollapsed state.\n\nFirst up we have the toggles, `.toggleGroup(int)` and\n`.toggleGroup(ExpandableGroup)`. These are handy for when you control the\nstates explicitly.\n\n```java\npublic class GenreActivity extends Activity {\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n\n    ...\n\n    Button showAllToggle = findViewById(R.id.show_all);\n    showAllToggle.setOnClickListener(new View.OnClickListener() {\n      public void onClick(View v) {\n        for (int i = adapter.groups().size() - 1; i \u003e= 0; i--) {\n          adapter.toggleGroup(i);\n        }\n      }\n    });\n\n  }\n}\n```\n\nWe also expose explicit methods to control the expanding and collapsing of\nspecific groups, `.expandGroup()` and `.collapseGroup()`. For example, to\nexpand the first group immediately:\n\n```java\npublic class GenreActivity extends Activity {\n\n  @Override\n  protected void onCreate(Bundle savedInstanceState) {\n\n    ...\n\n    adapter.expandGroup(0);\n\n  }\n}\n```\n\n## Adding Custom Expand / Collapse Animations\n\nIf you want to add a custom `Drawable` that animates based on a groups state, override the `expand()` and `collapse()` methods in your `GroupViewHolder`:\n\n``` java\npublic class GenreViewHolder extends GroupViewHolder {\n\n  ...\n\n  @Override\n  public void expand() {\n    animateExpand();\n  }\n\n  @Override\n  public void collapse() {\n    animateCollapse();\n  }\n}\n```\n\n## Listening to Expand/Collapse events\n\nIf you want register an `ExpandCollapseListener` outside of the adapter, you can simply call `setOnGroupExpandCollapseListener` on the `ExpandableRecyclerViewAdapter`\n\n``` java\n  adapter.setOnGroupExpandCollapseListener(new GroupExpandCollapseListener() {\n    @Override\n    public void onGroupExpanded(ExpandableGroup group) {\n\n    }\n\n    @Override\n    public void onGroupCollapsed(ExpandableGroup group) {\n\n    }\n  });\n```\n\n## Multiple Child and Group Types\n\nThe `MultiTypeExpandableRecyclerViewAdapter` allows subclasses to implement multiple different view types for both children and groups.\n\nContinuing 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`\n\n``` java\npublic class FavoriteArtistViewHolder extends ChildViewHolder {\n\n  private TextView favoriteArtistName;\n\n  public FavoriteArtistViewHolder(View itemView) {\n    super(itemView);\n    favoriteArtistName = (TextView) itemView.findViewById(R.id.list_item_favorite_artist_name);\n  }\n\n  public void setArtistName(String name) {\n    favoriteArtistName.setText(name);\n  }\n```\n\nJust like the regular `ArtistViewHolder`, `FavoriteArtistViewHolder` must extends `ChildViewHolder`.\n\nNext up, let's create a subclass of `MultiTypeExpandableRecyclerViewAdapter` called `MultiTypeGenreAdapter` and let's add two static `int`s representing our two artist view types:\n\n```java\npublic class MultiTypeGenreAdapter extends MultiTypeExpandableRecyclerViewAdapter\u003cGenreViewHolder, ChildViewHolder\u003e {\n\n\n  public static final int FAVORITE_VIEW_TYPE = 3;\n  public static final int ARTIST_VIEW_TYPE = 4;\n  ...\n```\n\nNotice we started used values \u003e 2. That's because `ExpandableListPosition.CHILD` and `ExpandableListPositon.GROUP` are `1` and `2` respectively so they are already taken.\n\nSince we only want a single view type for groups, we only need to override `getChildViewType()`. As `getGroupViewType()` will default to `ExpandableListPosition.GROUP`.\n\n``` java\n  @Override\n  public int getChildViewType(int position, ExpandableGroup group, int childIndex) {\n    if (((Genre) group).getItems().get(childIndex).isFavorite()) {\n      return FAVORITE_VIEW_TYPE;\n    } else {\n      return ARTIST_VIEW_TYPE;\n    }\n  }\n```\n\nSince we provided custom view types for our children, we must also override `isChild()`\n\n```java\n  @Override\n  public boolean isChild(int viewType) {\n    return viewType == FAVORITE_VIEW_TYPE || viewType == ARTIST_VIEW_TYPE;\n  }\n```\n\nAnd now, just like in any other `RecyclerView.Adapter` in our `onCreateChildViewHolder` and our `onBindChildViewHolder` we can use the provided parameters to switch on the different view tyeps:\n``` java\n  @Override\n  public ChildViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {\n    switch (viewType) {\n      case ARTIST_VIEW_TYPE:\n        View artist = from(parent.getContext()).inflate(R.layout.list_item_artist, parent, false);\n        return new ArtistViewHolder(artist);\n      case FAVORITE_VIEW_TYPE:\n        View favorite =\n            from(parent.getContext()).inflate(R.layout.list_item_favorite_artist, parent, false);\n        return new FavoriteArtistViewHolder(favorite);\n      default:\n        throw new IllegalArgumentException(\"Invalid viewType\");\n    }\n  }\n\n  @Override\n  public void onBindChildViewHolder(ChildViewHolder holder, int flatPosition, ExpandableGroup group,\n      int childIndex) {\n    int viewType = getItemViewType(flatPosition);\n    Artist artist = ((Genre) group).getItems().get(childIndex);\n    switch (viewType) {\n      case ARTIST_VIEW_TYPE:\n        ((ArtistViewHolder) holder).setArtistName(artist.getName());\n        break;\n      case FAVORITE_VIEW_TYPE:\n        ((FavoriteArtistViewHolder) holder).setArtistName(artist.getName());\n    }\n  }\n```\n\n## Expandable Check RecyclerView\nAn extension of `expandablerecyclerview` for checking single or multiple children within a group\n\nThe setup for the single and multi check versions is very similar to the `expandablerecyclerview` we walked through above. Here are a few of the notable differences...\n\n### CheckedExpandableGroup\n\nInstead of `ExpandableGroup` you must use `CheckedExpandableGroup`. `CheckedExpandableGroup` is a subclass of `ExpandableGroup` that uses a `SparseBooleanArray` to hold onto which of it's children are checked.\n\nThe `expandablecheckrecyclerview` library comes with two default implementations -  `SingleCheckExpandableGroup` and `MultiCheckExpandableGroup`.\n\n### Clearing Choices\n\nThe `CheckableChildRecyclerViewAdapter` has a `clearChoices()` which un checks any currently checked children.\n\n### CheckableChildViewHolder\n\nThe `CheckableChildViewHolder` is a subclass of `ChildViewHolder` that has a `Checkable` widget. The `Checkable` interface is initially not set, so in order to see your children view states update, you must set a `View` that implements `Checkable` in your view holder.\n\n``` java\npublic class SingleCheckArtistViewHolder extends CheckableChildViewHolder {\n\n  private CheckedTextView artistName;\n\n  public SingleCheckArtistViewHolder(View itemView) {\n    super(itemView);\n    artistName = (CheckedTextView) itemView.findViewById(R.id.list_item_singlecheck_artist_name);\n  }\n\n  @Override\n  public Checkable getCheckable() {\n    return artistName;\n  }\n  ...\n}\n```\n\n### Listening to Child Click Events\n\nThere is a custom callback for click events on children of a `CheckedExpandableGroup` which returns you the `View` of the row item that was clicked, the current checked state of the child, the containing `CheckedExpandableGroup` group and the index of the child that was clicked.\n\n``` java\n  adapter.setChildClickListener(new OnCheckChildClickListener() {\n    @Override\n    public void onCheckChildCLick(View v, boolean checked, CheckedExpandableGroup group,\n        int childIndex) {\n    }\n  });\n\n```\n\n## Sample App\n\nTo see the complete code for all the above examples along with unit tests for the adapters check out the `sample` app. The app has the following packages:\n\n### expand\nAn example of basic `ExpandableRecyclerViewAdapter`\n\n\u003cimg src=\"https://cloud.githubusercontent.com/assets/5386934/17074123/b9d1efca-502c-11e6-9c9f-fb6180ee337f.gif\" width=300 /\u003e\n\n\n### multicheck\nAn example of a `CheckableChildRecyclerViewAdapter` using `MultiCheckExpandableGroup`\n\n\u003cimg src=\"https://cloud.githubusercontent.com/assets/5386934/17074122/b9d0ec06-502c-11e6-8548-7647f63114dd.gif\" width=300 /\u003e\n\n\n### single check\nAn example of a `CheckableChildRecyclerViewAdapter` using `SingleCheckExpandableGroup`\n\n\u003cimg src=\"https://cloud.githubusercontent.com/assets/5386934/17074124/b9d22df0-502c-11e6-8b9c-d70e00c10909.gif\" width=300 /\u003e\n\n\n### multi type\nAn example of a `MultiTypeExpandableRecyclerViewAdapter` using two different child view holders\n\n\u003cimg src=\"https://cloud.githubusercontent.com/assets/5386934/17262690/ac0eeb9e-5591-11e6-9809-8b76644defee.gif\" width=300/\u003e\n\n## Contributing\n\nSee the [CONTRIBUTING] document. Thank you, [contributors]!\n\n## License\n\nExpandable RecyclerView is Copyright (c) 2016 thoughtbot, inc. It is free software, and may be redistributed under the terms specified in the [LICENSE] file.\n\n## About\n\nExpandable RecyclerView is maintained by [@mandybess](https://github.com/mandybess)\n\n![thoughtbot](https://thoughtbot.com/logo.png)\n\nExpandable RecyclerView is maintained and funded by thoughtbot, inc. The names and logos for thoughtbot are trademarks of thoughtbot, inc.\n\nWe love open source software! See [our other projects][tools] or [hire us][hire] to help build your product.\n\n  [tools]: https://thoughtbot.com/tools?utm_source=github\n  [hire]: https://thoughtbot.com/hire-us?utm_source=github\n  [LICENSE]: /LICENSE\n  [CONTRIBUTING]: CONTRIBUTING.md\n  [contributors]: https://github.com/thoughtbot/expandable-recycler-view/graphs/contributors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthoughtbot%2Fexpandable-recycler-view","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthoughtbot%2Fexpandable-recycler-view","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthoughtbot%2Fexpandable-recycler-view/lists"}