how to make an expandable list of cardviews?

16,019

If you don't want to use an external library, you can make your CardView expand like this:

Layout file for an expandable CardView

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="48dp"
            >

            <TextView
                android:id="@+id/textView_name"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="10dp"
                android:textSize="18sp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold" />

            <!--My dropdown Button -->
            <RelativeLayout
                android:id="@+id/button"
                android:layout_width="48dp"
                android:layout_height="48dp"
                android:layout_gravity="end"
                android:layout_alignParentRight="true"
                android:gravity="center"
                >

                <View
                    android:layout_width="12dp"
                    android:layout_height="12dp"
                    android:background="@drawable/triangle"
                    android:layout_alignParentRight="false"
                    android:layout_alignParentEnd="false" />
            </RelativeLayout>
        </RelativeLayout>
         <!--The layout below is my ExpandableLayout -->
        <LinearLayout
            android:id="@+id/expandableLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >

            <android.support.v7.widget.AppCompatImageView
                android:id="@+id/imageView_Owner"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/textView_Owner"
                    android:textSize="16sp"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
                <TextView
                    android:id="@+id/textView_OwnerUrl"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />


            </LinearLayout>

        </LinearLayout>

    </LinearLayout>

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

My ExpandableRecyclerAdapter Class

import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.squareup.picasso.Picasso;
import java.util.List;


public class ExpandableRecyclerAdapter extends RecyclerView.Adapter<ExpandableRecyclerAdapter.ViewHolder> {

private List<Repo> repos;
private SparseBooleanArray expandState = new SparseBooleanArray();
private Context context;

public ExpandableRecyclerAdapter(List<Repo> repos) {
    this.repos = repos;
    //set initial expanded state to false
    for (int i = 0; i < repos.size(); i++) {
        expandState.append(i, false);
    }
}

@Override
public ExpandableRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    this.context = viewGroup.getContext();
    View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.expandable_card_row, viewGroup, false);
    return new ViewHolder(view);
}

@Override
public void onBindViewHolder(final ExpandableRecyclerAdapter.ViewHolder viewHolder, final  int i) {

    viewHolder.setIsRecyclable(false);

    viewHolder.tvName.setText(repos.get(i).getName());

    viewHolder.tvOwnerLogin.setText("Owner: " +repos.get(i).getOwner().getLogin());
    viewHolder.tvOwnerUrl.setText(repos.get(i).getOwner().getUrl());

    Picasso.with(context)
            .load(repos.get(i).getOwner().getImageUrl())
            .resize(500, 500)
            .centerCrop()
            .into(viewHolder.ivOwner);

    //check if view is expanded
    final boolean isExpanded = expandState.get(i);
    viewHolder.expandableLayout.setVisibility(isExpanded?View.VISIBLE:View.GONE);

    viewHolder.buttonLayout.setRotation(expandState.get(i) ? 180f : 0f);
    viewHolder.buttonLayout.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            onClickButton(viewHolder.expandableLayout, viewHolder.buttonLayout,  i);
        }
    });
}

@Override
public int getItemCount() {
    return repos.size();
}

public class ViewHolder extends RecyclerView.ViewHolder{

    private TextView tvName,tvOwnerLogin, tvOwnerUrl;
    private ImageView ivOwner;
    public RelativeLayout buttonLayout;
    public LinearLayout expandableLayout;

    public ViewHolder(View view) {
        super(view);

        tvName = (TextView)view.findViewById(R.id.textView_name);
        tvId = (TextView)view.findViewById(R.id.textView_id);
        tvUrl = (TextView)view.findViewById(R.id.textView_url);
        tvOwnerLogin = (TextView)view.findViewById(R.id.textView_Owner);
        tvOwnerUrl = (TextView)view.findViewById(R.id.textView_OwnerUrl);
        ivOwner = (ImageView) view.findViewById(R.id.imageView_Owner);

        buttonLayout = (RelativeLayout) view.findViewById(R.id.button);
        expandableLayout = (LinearLayout) view.findViewById(R.id.expandableLayout);
    }
}

private void onClickButton(final LinearLayout expandableLayout, final RelativeLayout buttonLayout, final  int i) {

    //Simply set View to Gone if not expanded
    //Not necessary but I put simple rotation on button layout
    if (expandableLayout.getVisibility() == View.VISIBLE){
        createRotateAnimator(buttonLayout, 180f, 0f).start();
        expandableLayout.setVisibility(View.GONE);
        expandState.put(i, false);
    }else{
        createRotateAnimator(buttonLayout, 0f, 180f).start();
        expandableLayout.setVisibility(View.VISIBLE);
        expandState.put(i, true);
    }
}

//Code to rotate button
private ObjectAnimator createRotateAnimator(final View target, final float from, final float to) {
    ObjectAnimator animator = ObjectAnimator.ofFloat(target, "rotation", from, to);
    animator.setDuration(300);
    animator.setInterpolator(new LinearInterpolator());
    return animator;
}
}

In your Activity/Fragment, set up RecyclerView like this

private RecyclerView recyclerView;
private List<Repo> data;

recyclerView = (RecyclerView)findViewById(R.id.card_recycler_view);
recyclerView.setHasFixedSize(true);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
//fetch data and on ExpandableRecyclerAdapter
recyclerView.setAdapter(new ExpandableRecyclerAdapter(data));

So, its quite simple to create an expandable CardView without external Library. The code is almost exactly the same as using a normal CardView with RecyclerView, the main change is setting the View.GONE/View.VISIBLE on button click.

Share:
16,019
Javad Karbasian
Author by

Javad Karbasian

Updated on July 28, 2022

Comments

  • Javad Karbasian
    Javad Karbasian over 1 year

    I searched a lot to find a direct and clear solution for I think a popular problem but unfortunately I couldn't find it.

    We want to have a list of cardviews so each card bind to a specific data and each card has a list inside that shows meta or detail data about its parent.

    So we have a nested list inside a cardview.

    With a simple search, we know that we should use expanded list view which parents items are cardviews and we must have another layout for its child.

    So when you click on cards a list of items appears below of your cards on the root. But we want to show child list inside of cards?

    And there is no access to the something like child list id or any other things to refer to a transition animation and change of layout.

    So the clear question is how to add a listview inside a cardview?

    i follow my work by using tablelayout and table row. and prefer not to use external libraries for stability and version problems. this is what i mean.

    Image that describe my question

    image that describe my question

  • Ojonugwa Jude Ochalifu
    Ojonugwa Jude Ochalifu about 7 years
    How do you add it to the cardview? Please provide complete answers
  • Asesha George
    Asesha George almost 7 years
    no problem at all. simple clean perfect. nice one, thank you
  • Gulbala Salamov
    Gulbala Salamov over 5 years
    Thank you very much. Worked for me too
  • Batz
    Batz over 5 years
    Thanks, Worked for me!
  • abdu
    abdu about 4 years
    Thank you, saved my time