Expandable list with RecyclerView?

AndroidAndroid Recyclerview

Android Problem Overview


It's possible to use expandable list items with new RecyclerView? Like ExpandableListView?

Android Solutions


Solution 1 - Android

This is simple to do with the stock LayoutManagers, it all depends on how you manage your adapter.

When you want to expand a section you just add new items to your adapter after the header. Remember to call notifyItemRangeInserted when you do this. To collapse a section you simply remove the relevant items, and call notifyItemRangeRemoved(). For any data changes that are appropriately notified, the recycler view will animate the views. When adding items, an area to be filled with the new items is made, with the new items fading in. Removal is the opposite. All you need to do besides the adapter stuff is to style your views to convey the logical structure to the user.

Update: Ryan Brooks has now written an article on how to do this.

Solution 2 - Android

Get the sample code implementation from here

Set ValueAnimator inside onClick of ViewHolder

@Override
public void onClick(final View view) {
    if (mOriginalHeight == 0) {
        mOriginalHeight = view.getHeight();
    }
    ValueAnimator valueAnimator;
    if (!mIsViewExpanded) {
        mIsViewExpanded = true;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
    } else {
        mIsViewExpanded = false;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
    }
    valueAnimator.setDuration(300);
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer value = (Integer) animation.getAnimatedValue();
            view.getLayoutParams().height = value.intValue();
            view.requestLayout();
        }
    });
    valueAnimator.start();

}

Here is the final code

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView mFriendName;
    private int mOriginalHeight = 0;
    private boolean mIsViewExpanded = false;


    public ViewHolder(RelativeLayout v) {
        super(v);
        mFriendName = (TextView) v.findViewById(R.id.friendName);
        v.setOnClickListener(this);
    }

    @Override
    public void onClick(final View view) {
        if (mOriginalHeight == 0) {
            mOriginalHeight = view.getHeight();
        }
        ValueAnimator valueAnimator;
        if (!mIsViewExpanded) {
            mIsViewExpanded = true;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
        } else {
            mIsViewExpanded = false;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
        }
        valueAnimator.setDuration(300);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                view.getLayoutParams().height = value.intValue();
                view.requestLayout();
            }
        });
        valueAnimator.start();
    
    }
}

Solution 3 - Android

https://github.com/gabrielemariotti/cardslib

This library has an implementation of an expandable list with a recyclerview (refer to the demo app under "CardViewNative" --> "List, Grid, and RecyclerView" --> "Expandable cards"). It also has a lot of other cool combinations of cards/lists.

Solution 4 - Android

Someone complained about that the above mentioned solution is not usable with a listview as expandable content. But there's a simple solution: create a listview and fill this listview manually with your rows.

Solution for the lazy ones: there's a simple solution if you don't want to change your code to much. Just manually use your adapter to create views and add them to the LinearLayout.

Here's the example:

if (mIsExpanded)
{
	// llExpandable... is the expandable nested LinearLayout
	llExpandable.removeAllViews();
	final ArrayAdapter<?> adapter = ... // create your adapter as if you would use it for a ListView
	for (int i = 0; i < adapter.getCount(); i++)
	{
		View item = adapter.getView(i, null, null);
		// if you want the item to be selectable as if it would be in a default ListView, then you can add following code as well:
		item.setBackgroundResource(Functions.getThemeReference(context, android.R.attr.selectableItemBackground));
		item.setTag(i);
		item.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// item would be retrieved with: 
				// adapter.getItem((Integer)v.getTag())
			}
		});
		llExpandable.addView(item);
	}
	ExpandUtils.expand(llExpandable, null, 500);
}
else
{
	ExpandUtils.collapse(llExpandable, null, 500);
}

helper functions: getThemeReference

public static int getThemeReference(Context context, int attribute)
{
    TypedValue typeValue = new TypedValue();
    context.getTheme().resolveAttribute(attribute, typeValue, false);
    if (typeValue.type == TypedValue.TYPE_REFERENCE)
    {
        int ref = typeValue.data;
        return ref;
    }
    else
    {
        return -1;
    }
}

helper class: ExpandUtils

Kavin Varnan postet already how to animate a layout... But if you want to use my class, feel free to do so, I posted a gist: https://gist.github.com/MichaelFlisar/738dfa03a1579cc7338a

Solution 5 - Android

You can use ExpandableLayout that like a smooth expand/collapse animation CheckBox, so you can use it as CheckBox in ListView and RecyclerView.

https://github.com/KyoSherlock/ExpandableLayout

Solution 6 - Android

This is the sample code for what is mentioned by @TonicArtos to add and remove Items and to animate it while doing, this is taken from RecyclerView Animations and GitHub sample

1) Add Listener inside your onCreateViewHolder() to register for onClick

2) Create your custom OnClickListener inside your Adapter

private View.OnClickListener mItemListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        TextView tv = (TextView) v.findViewById(R.id.tvItems);
        String selected = tv.getText().toString();
        boolean checked = itemsList.get(recyclerView.getChildAdapterPosition(v)).isChecked();

        switch (selected){
            case "Item1":
                if(checked){
                    deleteItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                }else {
                    addItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                }
                break;
            case "Item2":
                if(checked){
                    deleteItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                }else {
                    addItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                }
                break;                 
            default:
                //In my case I have checkList in subItems,
                //checkItem(v);
                break;
        }
    }
};

3) Add your addItem() and deleteItem()

private void addItem(View view){
    int position = recyclerView.getChildLayoutPosition(view);
    if (position != RecyclerView.NO_POSITION){
        navDrawItems.add(position+1,new mObject());
        navDrawItems.add(position+2,new mObject());
        notifyItemRangeInserted(position+1,2);
    }
}


private void deleteItem(View view) {
    int position = recyclerView.getChildLayoutPosition(view);
    if (position != RecyclerView.NO_POSITION) {
        navDrawItems.remove(position+2);
        navDrawItems.remove(position+1);
        notifyItemRangeRemoved(position+1,2);
    }
}

4) If your RecyclerViewAdapter is not in the same Activity as Recycler View, pass instance of recyclerView to the Adapter while creating

5) itemList is a ArrayList of type mObject which helps maintain states of item (Open/Close) , name, type of Item(subItems/mainItem) and set Theme based on values

public class mObject{
    private String label;
    private int type;
    private boolean checked;
} 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDariusz RusinView Question on Stackoverflow
Solution 1 - AndroidTonic ArtosView Answer on Stackoverflow
Solution 2 - AndroidKavin VarnanView Answer on Stackoverflow
Solution 3 - AndroidXinzzView Answer on Stackoverflow
Solution 4 - Androidprom85View Answer on Stackoverflow
Solution 5 - Androiduser2914737View Answer on Stackoverflow
Solution 6 - AndroidUjjuView Answer on Stackoverflow