Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to make an expandable list of cardviews?

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

like image 380
Javad Karbasian Avatar asked Jul 07 '16 03:07

Javad Karbasian


People also ask

What is an Expandable list view?

A view that shows items in a vertically scrolling two-level list. This differs from the ListView by allowing two levels: groups which can individually be expanded to show its children. The items come from the ExpandableListAdapter associated with this view.


1 Answers

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.

like image 138
GraSim Avatar answered Oct 15 '22 20:10

GraSim