Custom RecyclerView with Dynamic ExpandableListview in Android Example

So now we will work on the above title as i have mentioned above. i am sorry i am some kind of Lazy but i can assure you i am a productive programmer 🙂

So lest get start it:

We need the following files to create a recycler view with Expandable listview

Java Files

1- DividerItemDecoration
2- Custom_RecycelerView
3- ExpandableListAdapter

XML Files

1: expandable_list_header.xml
2: rcycler_view.xml

First of all Lets have a look at gradle what dependecy are required to run the above Program:


 

All the java files code :

You can also just copy the code and without creating a java file just point your cursor onto the folder and press
ctrl+v in windows
cmd+v in MAC

Custom_RecycelerView.java

package info.infiniteloops.pkg_recycler_view;

import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;

import com.futuremind.recyclerviewfastscroll.FastScrollBubble;
import com.futuremind.recyclerviewfastscroll.FastScroller;
import com.google.android.gms.ads.AdListener;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;
import com.infiniteloopsinc.gk.adapter.ExpandableListAdapter;
import com.infiniteloopsinc.gk.externaldb.DatabaseAccess;

import java.util.ArrayList;
import java.util.List;

public class Custom_RecycelerView extends AppCompatActivity implements SearchView.OnQueryTextListener{
    RecyclerView recyclerview;
    ExpandableListAdapter mAdapter;
    List<ExpandableListAdapter.Item> data;
    FastScroller fastScroller;
    FastScrollBubble fastScrollBubble;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_all__series__question);
       
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbarMain);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setDisplayShowHomeEnabled(true);

        databaseAccess = DatabaseAccess.getInstance(this);
        databaseAccess.open();
        Boolean b= getIntent().getBooleanExtra("isWorld",false);
        if(b) {
            quesList = databaseAccess.getAllWorldQuestions_answer();
        }else{
            quesList = databaseAccess.getAllIndianQuestions_answer();

        }
        recyclerview = (RecyclerView) findViewById(R.id.recyclerview);
        fastScroller = (FastScroller) findViewById(R.id.fastscroll);
        recyclerview.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        data = new ArrayList<>();

        for(int i=0;i<quesList.size();i++) {
            ExpandableListAdapter.Item places = new ExpandableListAdapter.Item(ExpandableListAdapter.HEADER, quesList.get(i).getQuestion(),false,quesList.get(i).getId());

            places.invisibleChildren = new ArrayList<>();
            if(quesList.get(i).getRight_answer().toString().equals("A"))
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_one(),true,quesList.get(i).getId()));
            else
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_one(),false,quesList.get(i).getId()));

            if(quesList.get(i).getRight_answer().toString().equals("B"))
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_two(),true,quesList.get(i).getId()));
            else
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_two(),false,quesList.get(i).getId()));
            if(quesList.get(i).getRight_answer().toString().equals("C"))
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_three(),true,quesList.get(i).getId()));
            else
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_three(),false,quesList.get(i).getId()));
            if(quesList.get(i).getRight_answer().toString().equals("D"))
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_four(),true,quesList.get(i).getId()));
            else
                places.invisibleChildren.add(new ExpandableListAdapter.Item(ExpandableListAdapter.CHILD, quesList.get(i).getOpt_four(),false,quesList.get(i).getId()));
            data.add(places);

        }
        recyclerview.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
        mAdapter = new ExpandableListAdapter(data);

        recyclerview.setAdapter(mAdapter);
        //has to be called AFTER RecyclerView.setAdapter()
        fastScroller.setRecyclerView(recyclerview);
        fastScroller.setBubbleColor(0xffff0000);
        fastScroller.setHandleColor(0xffff0000);
        fastScroller.setBubbleTextAppearance(R.style.StyledScrollerTextAppearance);

    }
    public FastScroller getFastScroller(){
        return fastScroller;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.search_menu, menu);
        final MenuItem item = menu.findItem(R.id.action_search);
        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
        searchView.setOnQueryTextListener(this);

        return true;
    }
    @Override
    public boolean onQueryTextChange(String query) {
        final List<ExpandableListAdapter.Item> filteredModelList = filter(data, query);
        mAdapter.animateTo(filteredModelList);
        recyclerview.scrollToPosition(0);
        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }
    private List<ExpandableListAdapter.Item> filter(List<ExpandableListAdapter.Item> models, String query) {
        query = query.toLowerCase();

        final List<ExpandableListAdapter.Item> filteredModelList = new ArrayList<>();
        for (ExpandableListAdapter.Item model : models) {
            final String text = model.text.toString().toLowerCase();
            if (text.contains(query)) {
                filteredModelList.add(model);
            }
        }
        return filteredModelList;
    }

}




<h4>DividerItemDecoration.java</h4>



package info.infiniteloops.pkg_recycler_view;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
 
/**
 * Created by Lincoln on 30/10/15.
 */
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
 
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };
 
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
 
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
 
    private Drawable mDivider;
 
    private int mOrientation;
 
    public DividerItemDecoration(Context context, int orientation) {
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        mDivider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }
 
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }
 
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            drawVertical(c, parent);
        } else {
            drawHorizontal(c, parent);
        }
    }
 
    public void drawVertical(Canvas c, RecyclerView parent) {
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
 
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
 
    public void drawHorizontal(Canvas c, RecyclerView parent) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
 
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
                    .getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }
 
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST) {
            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
        } else {
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
        }
    }
}




<h4>ExpandableListAdapter.java</h4>



package info.infiniteloops.pkg_recycler_view;

import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.infiniteloopsinc.gk.R;
import com.infiniteloopsinc.gk.externaldb.DatabaseAccess;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by anandbose on 09/06/15.
 */
public class ExpandableListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    public static final int HEADER = 0;
    public static final int CHILD = 1;
    Context c;
    private List<Item> data;

    public ExpandableListAdapter(List<Item> data) {
        this.data = data;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) {
        View view = null;
        Context context = parent.getContext();
        c = context;
        float dp = context.getResources().getDisplayMetrics().density;
        int subItemPaddingLeft = (int) (18 * dp);
        int subItemPaddingTopAndBottom = (int) (5 * dp);
        switch (type) {
            case HEADER:
                LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.list_header, parent, false);
                ListHeaderViewHolder header = new ListHeaderViewHolder(view);
                return header;
            case CHILD:
                TextView itemTextView = new TextView(context);
                itemTextView.setPadding(subItemPaddingLeft, subItemPaddingTopAndBottom, 0, subItemPaddingTopAndBottom);
                itemTextView.setTextColor(0x88000000);
                itemTextView.setLayoutParams(
                        new ViewGroup.LayoutParams(
                                ViewGroup.LayoutParams.MATCH_PARENT,
                                ViewGroup.LayoutParams.WRAP_CONTENT));
                return new RecyclerView.ViewHolder(itemTextView) {
                };
        }
        return null;
    }

    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        final Item item = data.get(position);
        switch (item.type) {
            case HEADER:
                final ListHeaderViewHolder itemController = (ListHeaderViewHolder) holder;
                itemController.refferalItem = item;
                itemController.header_title.setText(item.text);
                if (item.invisibleChildren == null) {
                    itemController.btn_expand_toggle.setImageResource(R.drawable.close);
                } else {
                    itemController.btn_expand_toggle.setImageResource(R.drawable.open);
                }
                itemController.btn_expand_toggle.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (item.invisibleChildren == null) {
                            item.invisibleChildren = new ArrayList<Item>();
                            int count = 0;
                            int pos = data.indexOf(itemController.refferalItem);
                            while (data.size() > pos + 1 && data.get(pos + 1).type == CHILD) {
                                item.invisibleChildren.add(data.remove(pos + 1));
                                count++;
                            }
                            notifyItemRangeRemoved(pos + 1, count);
                            itemController.btn_expand_toggle.setImageResource(R.drawable.open);
                        } else {
                            int pos = data.indexOf(itemController.refferalItem);
                            int index = pos + 1;
                            for (Item i : item.invisibleChildren) {
                                data.add(index, i);
                                index++;
                            }
                            notifyItemRangeInserted(pos + 1, index - pos - 1);
                            itemController.btn_expand_toggle.setImageResource(R.drawable.close);
                            item.invisibleChildren = null;
                        }
                    }
                });
                itemController.header_title.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (item.invisibleChildren == null) {
                            item.invisibleChildren = new ArrayList<Item>();
                            int count = 0;
                            int pos = data.indexOf(itemController.refferalItem);
                            while (data.size() > pos + 1 && data.get(pos + 1).type == CHILD) {
                                item.invisibleChildren.add(data.remove(pos + 1));
                                count++;
                            }
                            notifyItemRangeRemoved(pos + 1, count);
                            itemController.btn_expand_toggle.setImageResource(R.drawable.open);
                        } else {
                            int pos = data.indexOf(itemController.refferalItem);
                            int index = pos + 1;
                            for (Item i : item.invisibleChildren) {
                                data.add(index, i);
                                index++;
                            }
                            notifyItemRangeInserted(pos + 1, index - pos - 1);
                            itemController.btn_expand_toggle.setImageResource(R.drawable.close);
                            item.invisibleChildren = null;
                        }
                    }
                });
                itemController.imgBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        itemController.databaseAccess = DatabaseAccess.getInstance(c);
                        itemController.databaseAccess.open();
                        int drawableId = (Integer)itemController.imgBtn.getTag(R.id.imgFav);
                        if(drawableId == R.drawable.ic_fav_grey){
                            itemController.databaseAccess.addFav(data.get(position).quesid,"1");
                            itemController.imgBtn.setTag(R.id.imgFav, R.drawable.ic_fav_red); //Set
                            itemController.imgBtn.setImageResource(R.drawable.ic_fav_red);
                            Toast.makeText(c,"Bookmark added",Toast.LENGTH_SHORT).show();
                        }else if(drawableId == R.drawable.ic_fav_red){
                            itemController.databaseAccess.addFav(data.get(position).quesid,"0");
                            itemController.imgBtn.setTag(R.id.imgFav, R.drawable.ic_fav_grey); //Set
                            itemController.imgBtn.setImageResource(R.drawable.ic_fav_grey);
                            Toast.makeText(c,"Removed from bookmark",Toast.LENGTH_SHORT).show();

                        }

                    }
                });
                break;
            case CHILD:
                TextView itemTextView = (TextView) holder.itemView;
                if(data.get(position).b) {
                    itemTextView.setTextColor(ContextCompat.getColor(c, R.color.green));
                }else{
                    itemTextView.setTextColor(ContextCompat.getColor(c, R.color.black));

                }
                itemTextView.setText(data.get(position).text);

                break;
        }
    }

    @Override
    public int getItemViewType(int position) {
        return data.get(position).type;
    }

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

    private static class ListHeaderViewHolder extends RecyclerView.ViewHolder {
        public TextView header_title;
        public ImageView btn_expand_toggle;
        public ImageButton imgBtn;
        public Item refferalItem;
        public DatabaseAccess databaseAccess;

        public ListHeaderViewHolder(View itemView) {
            super(itemView);
            try {
                header_title = (TextView) itemView.findViewById(R.id.header_title);
                btn_expand_toggle = (ImageView) itemView.findViewById(R.id.btn_expand_toggle);
                imgBtn = (ImageButton) itemView.findViewById(R.id.imgFav);
                imgBtn.setTag(R.id.imgFav, R.drawable.ic_fav_grey); //Set
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    public static class Item {
        public int type;
        public Boolean b;
        public String text;
        public String quesid;
        public List<Item> invisibleChildren;

        public Item() {
        }

        public Item(int type, String text,Boolean b,String quesid) {
            this.type = type;
            this.b = b;
            this.quesid = quesid;
            this.text = text;
        }
    }
    public void animateTo(List<Item> models) {
        applyAndAnimateRemovals(models);
        applyAndAnimateAdditions(models);
        applyAndAnimateMovedItems(models);
    }

    private void applyAndAnimateRemovals(List<Item> newModels) {
        for (int i = data.size() - 1; i >= 0; i--) {
            final Item model = data.get(i);
            if (!newModels.contains(model)) {
                removeItem(i);
            }
        }
    }

    private void applyAndAnimateAdditions(List<Item> newModels) {
        for (int i = 0, count = newModels.size(); i < count; i++) {
            final Item model = newModels.get(i);
            if (!data.contains(model)) {
                addItem(i, model);
            }
        }
    }

    private void applyAndAnimateMovedItems(List<Item> newModels) {
        for (int toPosition = newModels.size() - 1; toPosition >= 0; toPosition--) {
            final Item model = newModels.get(toPosition);
            final int fromPosition = data.indexOf(model);
            if (fromPosition >= 0 && fromPosition != toPosition) {
                moveItem(fromPosition, toPosition);
            }
        }
    }

    public Item removeItem(int position) {
        final Item model = data.remove(position);
        notifyItemRemoved(position);
        return model;
    }

    public void addItem(int position, Item model) {
        data.add(position, model);
        notifyItemInserted(position);
    }

    public void moveItem(int fromPosition, int toPosition) {
        final Item model = data.remove(fromPosition);
        data.add(toPosition, model);
        notifyItemMoved(fromPosition, toPosition);
    }
}


Our xml layouts are as follows : 



<h4>expandable_list_header.xml</h4>



<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_marginTop="3dp" android:layout_marginBottom="3dp" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content">
    <ImageButton android:layout_width="wrap_content" android:background="@android:color/transparent" android:src="@drawable/ic_fav_grey" android:id="@+id/imgFav" android:layout_height="match_parent" />
    <TextView android:id="@+id/header_title" android:textColor="@color/black" android:textSize="18sp" android:layout_marginLeft="5dp" android:gravity="center_vertical" android:text="Hello World so this is my world and i wont get back down" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:maxLines="10" />
    <ImageView android:id="@+id/btn_expand_toggle" android:src="@drawable/open" android:layout_width="30dp" android:layout_height="match_parent" android:layout_weight="0"/>
</LinearLayout>

rcycler_view.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto">
 
    <android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="vertical" />
 
    <com.futuremind.recyclerviewfastscroll.FastScroller android:id="@+id/fastscroll" android:orientation="vertical" android:layout_width="wrap_content" app:fastscroll__handleColor="#8f93d1" app:fastscroll__bubbleColor="#5e64ce" android:layout_height="match_parent" android:layout_alignParentRight="true"/>
</RelativeLayout>

If in case you are unable to find any file you can check out from the GitHub Repositiory

For project fork me on GitHub