Expandable Recyclerview For Android With Example

I am using Mindorks Placeholder library to create an Expandable Recyclerview for android. Below I have explained how to create header and child views and also explained how to get data from Rest Apis into the expandable Recyclerview.

What is Recyclerview?

Android RecyclerView is a more advanced, powerful and flexible version of the ListView. Android RecyclerView is similar to ListView except that it forces us to use RecyclerView.ViewHolder class to hold the elements which are not a compulsion in ListView.

As the name suggests, Android RecyclerView is used to reuse cells when scrolling up and down by recycling the items in the list. Another improvement in RecyclerView is that it allows us to set the LayoutManagers dynamically at runtime, unlike the ListView which was only available in a Vertical scrolling List. RecyclerView allows us to set the following types of Layouts at runtime.

LinearLayoutManager: it supports both vertical and horizontal lists
StaggeredLayoutManager: it supports staggered lists
GridLayoutManager: it supports displaying grids as seen in GalleryView earlier

Steps to create Expandable Recyclerview


Swipe card view Android with Example[Updated]

Expandable Recyclerview For Android


Adding Dependencies

add Mindorks Placeholderview library into the app.gradle file.

compile 'com.mindorks:placeholderview:0.7.1'

Also, Add Retrofit And Recyclerview dependencies into the app.gradle file.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.0.2'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    //placeholderview
    compile 'com.mindorks:placeholderview:0.7.1'
    //cardview
    implementation 'com.android.support:cardview-v7:27.0.2'
    implementation 'com.android.support:recyclerview-v7:27.0.2'
    //Retrofit
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    //Glide
    implementation 'com.github.bumptech.glide:glide:4.6.1'
}

Rest API

http://velmm.com/apis/movielist.json

Response 

[{
        "category": "Latest",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/coco.jpg",
        "name": "Coco",
        "desc": "Coco is a 2017 American 3D computer-animated musical fantasy adventure film produced by Pixar"
    },
    {
        "category": "Latest",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/terminator_2.jpg",
        "name": "Terminator 2: Judgment Day 3D",
        "desc": "Similar to Cameron's Titanic 3D, Lightstorm Entertainment oversaw the work on the 3D version of Terminator 2, which took nearly a year to finish."
    },
    {
        "category": "Latest",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/dunkirk.jpg",
        "name": "Dunkirk",
        "desc": "Dunkirk is a 2017 war film written, directed, and co-produced by Christopher Nolan that depicts the Dunkirk evacuation of World War II. "
    },
    {
        "category": "Favorites",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/the_salesman.jpg",
        "name": "The Salesman",
        "desc": "The Salesman is a 2016 drama film written and directed by Asghar Farhadi and starring Taraneh Alidoosti and Shahab Hosseini. "
    },
    {
        "category": "Favorites",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/lion.png",
        "name": "Lion",
        "desc": "Lion is a 2016 Australian biographical drama film directed by Garth Davis (in his feature debut) and written by Luke Davies, based on the non-fiction book A Long Way Home by Saroo Brierley."
    },
    {
        "category": "High Rated",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/star_war.jpg",
        "name": "Star Wars: The Last Jedi",
        "desc": "Star Wars: The Last Jedi (also known as Star Wars: Episode VIII – The Last Jedi) is a 2017 American epic space opera film written and directed by Rian Johnson."
    },
    {
        "category": "High Rated",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/thor_ragnarok.jpg",
        "name": "Thor: Ragnarok",
        "desc": "Thor: Ragnarok is a 2017 American superhero film based on the Marvel Comics character Thor, produced by Marvel Studios and distributed by Walt Disney Studios Motion Pictures."
    },
    {
        "category": "High Rated",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/blade_runner_2049.jpg",
        "name": "Blade Runner 2049",
        "desc": "Blade Runner 2049 is a 2017 American science fiction film directed by Denis Villeneuve and written by Hampton Fancher and Michael Green. "
    },
    {
        "category": "High Rated",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/borg_mcenroe.jpg",
        "name": "Borg McEnroe",
        "desc": "Borg McEnroe also known as Borg vs McEnroe, is a 2017 internationally co-produced multi-language biographical sports drama film focusing on the famous rivalry between famous tennis players "
    },
    {
        "category": "High Rated",
        "imageUrl": "http://velmm.com/images/bottom_navigationview/wonder.jpg",
        "name": "Wonder",
        "desc": "Wonder is a 2017 American drama film directed by Stephen Chbosky and written by Jack Thorne , Steve Conrad and Stephen Chbosky based on the 2012 novel of the same name by R.J. Palacio."
    }
]

Related Post: shimmer effect For Android Recyclerview Example

Related Post: Retrofit Android Example With Recyclerview [Download]


Setup Retrofit 

Retrofit is a REST Client library (Helper Library) used in Android and Java to create an HTTP request and also to process the HTTP response from a REST API. Since, It’s created by Square, you can also use retrofit to receive data structures other than JSON. For example, SimpleXML and Jackson. Before we continue, let’s briefly define REST Client and REST API in our context.

Movie.java

import com.google.gson.annotations.SerializedName;

public class Movie {

  @SerializedName("name")
  private String name;
  @SerializedName("desc")
  private String desc;
  @SerializedName("imageUrl")
  private String imageUrl;
  @SerializedName("category")
  private String categoty;

  public Movie(String name, String desc, String imageUrl, String categoty) {
    this.name = name;
    this.desc = desc;
    this.imageUrl = imageUrl;
    this.categoty = categoty;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getDesc() {
    return desc;
  }

  public void setDesc(String desc) {
    this.desc = desc;
  }

  public String getImageUrl() {
    return imageUrl;
  }

  public void setImageUrl(String imageUrl) {
    this.imageUrl = imageUrl;
  }

  public String getCategoty() {
    return categoty;
  }

  public void setCategoty(String categoty) {
    this.categoty = categoty;
  }

  @Override
  public String toString() {
    return "Movie{" +
        "name='" + name + '\'' +
        ", desc='" + desc + '\'' +
        ", imageUrl='" + imageUrl + '\'' +
        ", categoty='" + categoty + '\'' +
        '}';
  }
}

ApiClient.java

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class ApiClient {

  private static Retrofit retrofit;
  private static String BASE_URL="http://velmm.com/apis/";

  public static Retrofit getInstance(){
    if(retrofit == null){
      retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
          .addConverterFactory(GsonConverterFactory.create())
          .build();
    }
    return retrofit;
  }
}

ApiInterface.java

import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;

public interface ApiInterface {
  @GET("movielist.json")
  Call<List<Movie>> getAllMovies();
}

Create Headerview

HeaderView.java

import android.content.Context;
import android.util.Log;
import android.widget.TextView;

import com.mindorks.placeholderview.annotations.Layout;
import com.mindorks.placeholderview.annotations.Resolve;
import com.mindorks.placeholderview.annotations.View;
import com.mindorks.placeholderview.annotations.expand.Collapse;
import com.mindorks.placeholderview.annotations.expand.Expand;
import com.mindorks.placeholderview.annotations.expand.Parent;
import com.mindorks.placeholderview.annotations.expand.SingleTop;

@Parent
@SingleTop
@Layout(R.layout.header_layout)
public class HeaderView {

    private static String TAG = "HeaderView";

    @View(R.id.header_text)
    TextView headerText;

    private Context mContext;
    private String mHeaderText;

    public HeaderView(Context context,String headerText) {
        this.mContext = context;
        this.mHeaderText = headerText;
    }

    @Resolve
    private void onResolve(){
        Log.d(TAG, "onResolve");
        headerText.setText(mHeaderText);
    }

    @Expand
    private void onExpand(){
        Log.d(TAG, "onExpand");
    }

    @Collapse
    private void onCollapse(){
        Log.d(TAG, "onCollapse");
    }
}

header_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="60dp"
  xmlns:tools="http://schemas.android.com/tools"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  app:cardBackgroundColor="#9e9d9d"
  android:layout_margin="5dp">

    <TextView
      android:id="@+id/header_text"
      android:textStyle="bold"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_gravity="center_vertical"
      android:gravity="center_vertical"
      android:padding="10dp"
      android:textAppearance="@style/TextAppearance.AppCompat.Large"
      android:textColor="@android:color/white"
      tools:text="@tools:sample/full_names"/>

</android.support.v7.widget.CardView>

Create ChildView 

ChildView.java

import android.content.Context;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.mindorks.placeholderview.annotations.Layout;
import com.mindorks.placeholderview.annotations.Resolve;
import com.mindorks.placeholderview.annotations.View;

@Layout(R.layout.child_layout)
public class ChildView {
    private static String TAG ="ChildView";

    @View(R.id.child_name)
    TextView textViewChild;

    @View(R.id.child_desc)
    TextView textViewDesc;

    @View(R.id.child_image)
    ImageView childImage;

    private Context mContext;
    private Movie movie;

    public ChildView(Context mContext, Movie movie) {
        this.mContext = mContext;
        this.movie = movie;
    }

    @Resolve
    private void onResolve(){
        Log.d(TAG,"onResolve");
        textViewChild.setText(movie.getName());
        Glide.with(mContext).load(movie.getImageUrl()).apply(RequestOptions.circleCropTransform()).into(childImage);
    }
}

child_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:card_view="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:id="@+id/card_view"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:padding="5dp"
  app:cardBackgroundColor="#c2c1c1"
  android:layout_marginLeft="20dp"
  android:layout_marginTop="5dp"
  android:layout_marginBottom="5dp"
  android:layout_marginRight="5dp"
  card_view:cardCornerRadius="10dp">


    <android.support.constraint.ConstraintLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:paddingBottom="5dp"
      android:paddingTop="5dp">

        <ImageView
          android:id="@+id/child_image"
          android:layout_width="70dp"
          android:layout_height="70dp"
          android:padding="5dp"
          android:src="#cfcfcf"/>

        <TextView
          android:id="@+id/child_name"
          android:layout_width="0dp"
          android:layout_height="wrap_content"
          android:layout_marginTop="8dp"
          android:layout_marginStart="8dp"
          android:layout_marginEnd="8dp"
          android:layout_alignParentEnd="true"
          android:layout_alignParentTop="true"
          android:layout_alignStart="@+id/child_desc"
          android:textStyle="bold"
          android:textAppearance="?android:attr/textAppearanceMedium"
          card_view:layout_constraintEnd_toEndOf="parent"
          card_view:layout_constraintStart_toEndOf="@+id/child_image"
          card_view:layout_constraintTop_toTopOf="@+id/child_image"
          tools:text="Large Text"/>

        <TextView
          android:id="@+id/child_desc"
          android:layout_width="0dp"
          android:layout_height="wrap_content"
          android:layout_marginTop="8dp"
          android:layout_marginStart="8dp"
          android:layout_marginEnd="8dp"
          android:layout_below="@+id/child_name"
          android:layout_toRightOf="@+id/child_image"
          android:textAppearance="?android:attr/textAppearanceSmall"
          card_view:layout_constraintEnd_toEndOf="parent"
          card_view:layout_constraintStart_toEndOf="@+id/child_image"
          card_view:layout_constraintTop_toBottomOf="@+id/child_name"
          tools:text="Small Text"/>
    </android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>

Finally, Add your data into expandable Recyclerview in MainActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.util.Log;
import android.view.View.OnClickListener;
import android.widget.Toast;
import com.mindorks.placeholderview.ExpandablePlaceHolderView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

  private Map<String,List<Movie>> categoryMap;

  private List<Movie> movieList;

    private ExpandablePlaceHolderView expandablePlaceHolderView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        movieList = new ArrayList<>();
        categoryMap = new HashMap<>();
        expandablePlaceHolderView = (ExpandablePlaceHolderView) findViewById(R.id.expandablePlaceHolder);
        
        loadData();

        expandablePlaceHolderView.setOnClickListener(new OnClickListener() {
          @Override
          public void onClick(android.view.View view) {
            Toast.makeText(getApplicationContext(),"Clixcked", view.getId()).show();
          }
        });

    }

    private void loadData(){

        ApiInterface apiInterface = ApiClient.getInstance().create(ApiInterface.class);
        apiInterface.getAllMovies().enqueue(new Callback<List<Movie>>() {
            @Override
            public void onResponse(Call<List<Movie>> call, Response<List<Movie>> response) {
              movieList = response.body();
              getHeaderAndChild(movieList);
            }

            @Override
            public void onFailure(Call<List<Movie>> call, Throwable t) {

            }
        });

    }

    private void getHeaderAndChild(List<Movie> movieList){

      for (Movie movie : movieList ){
        List<Movie> movieList1 = categoryMap.get(movie.getCategoty());
        if(movieList1 == null){
          movieList1 = new ArrayList<>();
        }
        movieList1.add(movie);
        categoryMap.put(movie.getCategoty(),movieList1);
      }

      Log.d("Map",categoryMap.toString());
      Iterator it = categoryMap.entrySet().iterator();
      while (it.hasNext()) {
        Map.Entry pair = (Map.Entry)it.next();
        Log.d("Key", pair.getKey().toString());
        expandablePlaceHolderView.addView(new HeaderView(this, pair.getKey().toString()));
        List<Movie> movieList1 = (List<Movie>) pair.getValue();
        for (Movie movie : movieList1){
          expandablePlaceHolderView.addView(new ChildView(this, movie));
        }
        it.remove();
      }
    }


}

Screenshot

Expandable Recyclerview Example

Expandable Recyclerview Example

Download

28 thoughts on - Expandable Recyclerview For Android With Example

  • Hi thanks for sharing this. I’m trying to adapt it a little bit for my project, the only difference being that I want to include an image in the header as well. The imagepath is in my json but I cannot figue out how to include in the hashmap so it can be sent to the HeaderView.java from MainActivity.java (expandablePlaceHolderView.addView(new HeaderView(this, pair.getKey().toString(),myOtherImagepath));).

    • add your imageview into header_layout. and load the url into imageview in headerview.java onResolve().

      • It’s working. Please have a look again.

  • when clicked child items… toast not showing

    • Add @click(viewid)
      Private void clicklistener() {}
      In your childview.

      • Thank u so much…. 🙂

        • Welcome.

          • Thank u so much bro… God bless u….:-)

          • Thanks for your visit. Do visit again

          • Yup… Sure…. 🙂 i have bookmarked ur website…

        • and how to use intent here?

          • Intent intent = new Intent (mcontext, secondactivity. Class);
            mcontext.startacticity(intent);

      • How to get viewid?

        • Which view Id? give me more details.

      • What is viewid here ?

        • Do you mean Child Textview Id?

  • when clicked child items… toast not showing

  • How can I change the orientation of child view, like grid

    • Try.
      expandablePlaceHolderView.setLayoutManager(new GridLayoutManager(this,2));

  • Hello.Thanks for the content. One thing, please. I would like to make a language dictionary like this in Kotlin. The question is: how do i show the translated word when i press the word and to hide it when i press the next word. I Would like to put a sound to..
    I need a general method to start. Should i do a recycleview ? But how? I’m a begginer in programing:)
    Thanks in advance!

    https://i.stack.imgur.com/N925p.jpg

    • I got your point. You better to use recyclerview and hide and show the view with transition animation you wanted. in this case, you don’t want to use the expandable recyclerview right?

      • https://imgur.com/a/PUiFd2Y

        Sorry for the bad link.
        I dont want to be a expandable Recycleview. just to hide the child view ( the translated word ) when i press to see another word text. Many thanks for the advice!!

        • Welcome. Keep visiting my site

  • I have to load huge load of data and here UI updating and JSON parsing happens on same method, i.e(getHeaderAndChild). When I try to solve it on rxjava, I cant separate the UI update part with the the json manipulation part and due to it the whole process has to be done on main thread..Can you please help me solve this issue

  • hey how to intent in child? i can’t intent in child

  • hi, thanks a lot for this project,
    i have one question, how the way to add onclick the item.
    i want to add, if the item clicked and then open new activity?
    i hope u can help me,
    Thank you So much!

  • how to delete one child item?

Leave a Reply

Your email address will not be published. Required fields are marked *