Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an expandable RecyclerView

Tags:

I'm trying to implement a recyclerview that behaves like my sketch below:

enter image description here

The idea is that there is a parent list, when an list item in the parent list is tapped, that list item reveals a child list that contains it's own data. when a list item is tapped in the child list, the value of that child is reflected and updates the value of the parent in the parent list item.

I've tried to get it working for the past 3 days to no avail. I tried using the AdvancedReyclerview library but to a beginner like me, it was a giant mess of things that didn't make sense especially when passing in the data. I copied and pasted files that I needed to get a minimal working version but I had no idea how to pass my data into the recyclerview and how to update it with the newly selected value.

Is it even possible to do what I'm trying to do or am I way out of my depth here?

If it's still difficult to understand, I can explain it more.

EDIT: someone recommended that I do this with the ExpandableListView rather than the RecyclerView. Any thoughts on that?

like image 868
fadelakin Avatar asked Apr 27 '15 04:04

fadelakin


People also ask

How do you use expandable list view?

Android ExpandableListView is a view that shows items in a vertically scrolling two-level list. It differs from a ListView by allowing two levels which are groups that can be easily expanded and collapsed by touching to view and their respective children items.


1 Answers

1.ExpandableRecyclerAdapter.class

public abstract class ExpandableRecyclerAdapter<T extends ExpandableRecyclerAdapter.ListItem> extends RecyclerView.Adapter<ExpandableRecyclerAdapter.ViewHolder> {     protected Context mContext;     protected List<T> allItems = new ArrayList<>();     protected List<T> visibleItems = new ArrayList<>();     private List<Integer> indexList = new ArrayList<>();     private SparseIntArray expandMap = new SparseIntArray();     private int mode;      protected static final int TYPE_HEADER = 1000;      private static final int ARROW_ROTATION_DURATION = 150;      public static final int MODE_NORMAL = 0;     public static final int MODE_ACCORDION = 1;      public ExpandableRecyclerAdapter(Context context) {     mContext = context;     }      public static class ListItem {     public int ItemType;      public ListItem(int itemType) {         ItemType = itemType;     }     }      @Override     public long getItemId(int i) {     return i;     }      @Override     public int getItemCount() {     return visibleItems == null ? 0 : visibleItems.size();     }      protected View inflate(int resourceID, ViewGroup viewGroup) {     return LayoutInflater.from(mContext).inflate(resourceID, viewGroup, false);     }      public class ViewHolder extends RecyclerView.ViewHolder {     public ViewHolder(View view) {         super(view);     }     }      public class HeaderViewHolder extends ViewHolder {     ImageView arrow;      public HeaderViewHolder(View view) {         super(view);          view.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 toggleExpandedItems(getLayoutPosition(),false);                 /*if(isExpanded(getLayoutPosition())){                     collapseItems(getLayoutPosition(),false);                 }else {                     expandItems(getLayoutPosition(),true);                 }*/             }         });     }      public HeaderViewHolder(View view, final ImageView arrow) {         super(view);          this.arrow = arrow;          arrow.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 handleClick();             }         });     }      protected void handleClick() {         if (toggleExpandedItems(getLayoutPosition(), false)) {             openArrow(arrow);         } else {             closeArrow(arrow);         }     }      public void bind(int position) {         arrow.setRotation(isExpanded(position) ? 90 : 0);     }     }      public boolean toggleExpandedItems(int position, boolean notify) {     if (isExpanded(position)) {         collapseItems(position, notify);         return false;     } else {         expandItems(position, notify);          if (mode == MODE_ACCORDION) {             collapseAllExcept(position);         }          return true;     }     }      public void expandItems(int position, boolean notify) {     int count = 0;     int index = indexList.get(position);     int insert = position;      for (int i=index+1; i<allItems.size() && allItems.get(i).ItemType != TYPE_HEADER; i++) {         insert++;         count++;         visibleItems.add(insert, allItems.get(i));         indexList.add(insert, i);     }      notifyItemRangeInserted(position + 1, count);      int allItemsPosition = indexList.get(position);     expandMap.put(allItemsPosition, 1);      if (notify) {         notifyItemChanged(position);     } }  public void collapseItems(int position, boolean notify) {     int count = 0;     int index = indexList.get(position);      for (int i=index+1; i<allItems.size() && allItems.get(i).ItemType != TYPE_HEADER; i++) {         count++;         visibleItems.remove(position + 1);         indexList.remove(position + 1);     }      notifyItemRangeRemoved(position + 1, count);      int allItemsPosition = indexList.get(position);     expandMap.delete(allItemsPosition);      if (notify) {         notifyItemChanged(position);     }     }   protected boolean isExpanded(int position) {     int allItemsPosition = indexList.get(position);     return expandMap.get(allItemsPosition, -1) >= 0; }  @Override public int getItemViewType(int position) {     return visibleItems.get(position).ItemType; }  public void setItems(List<T> items) {     allItems = items;     List<T> visibleItems = new ArrayList<>();     expandMap.clear();     indexList.clear();      for (int i=0; i<items.size(); i++) {         if (items.get(i).ItemType == TYPE_HEADER) {             indexList.add(i);             visibleItems.add(items.get(i));         }     }      this.visibleItems = visibleItems;     notifyDataSetChanged();     }    protected void removeItemAt(int visiblePosition) {     int allItemsPosition = indexList.get(visiblePosition);      allItems.remove(allItemsPosition);     visibleItems.remove(visiblePosition);      incrementIndexList(allItemsPosition, visiblePosition, -1);     incrementExpandMapAfter(allItemsPosition, -1);      notifyItemRemoved(visiblePosition); }  private void incrementExpandMapAfter(int position, int direction) {     SparseIntArray newExpandMap = new SparseIntArray();      for (int i=0; i<expandMap.size(); i++) {         int index = expandMap.keyAt(i);         newExpandMap.put(index < position ? index : index + direction, 1);     }      expandMap = newExpandMap;     }      private void incrementIndexList(int allItemsPosition, int visiblePosition, int direction) {     List<Integer> newIndexList = new ArrayList<>();      for (int i=0; i<indexList.size(); i++) {         if (i == visiblePosition) {             if (direction > 0) {                 newIndexList.add(allItemsPosition);             }         }          int val = indexList.get(i);         newIndexList.add(val < allItemsPosition ? val : val + direction);         }      indexList = newIndexList;     }      public void collapseAll() {     collapseAllExcept(-1);     }      public void collapseAllExcept(int position) {     for (int i=visibleItems.size()-1; i>=0; i--) {         if (i != position && getItemViewType(i) == TYPE_HEADER) {             if (isExpanded(i)) {                 collapseItems(i, true);             }         }     }     }      public void expandAll() {     for (int i=visibleItems.size()-1; i>=0; i--) {         if (getItemViewType(i) == TYPE_HEADER) {             if (!isExpanded(i)) {                 expandItems(i, true);             }         }     }     }      public static void openArrow(View view) {     view.animate().setDuration(ARROW_ROTATION_DURATION).rotation(180);      }      public static void closeArrow(View view) {     view.animate().setDuration(ARROW_ROTATION_DURATION).rotation(0);     }      public int getMode() {     return mode;     }      public void setMode(int mode) {     this.mode = mode;     } } 

2.activity_main

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"  android:layout_width="match_parent" android:layout_height="match_parent">  <android.support.v7.widget.RecyclerView      android:id="@+id/main_recycler"     android:layout_width="match_parent"     android:layout_height="match_parent" /> 

3.item_header

<?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:orientation="horizontal" android:padding="@dimen/standard_padding">  <LinearLayout     android:id="@+id/lnr_1"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:layout_centerHorizontal="true">      <TextView         android:id="@+id/txt_header_address"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:drawableLeft="@mipmap/ic_usa"         android:gravity="center"         android:text="Beverly Hills"         android:textStyle="bold" />  </LinearLayout>  <ImageView     android:id="@+id/img_arrow"     android:layout_width="@dimen/arrow_size"     android:layout_height="@dimen/arrow_size"     android:layout_alignParentRight="true"     android:src="@mipmap/arrow" />  <TextView     android:id="@+id/txt_header_name"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:layout_alignParentLeft="true"     android:layout_alignParentStart="true"     android:layout_centerVertical="true"     android:text="Home"     android:textStyle="bold" /> 

4.item_content.xml

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical">  <RelativeLayout     android:id="@+id/rcl_header_btn"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:orientation="horizontal">      <Button         style="?android:attr/borderlessButtonStyle"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:text="@string/btn_cancle" />      <Button         style="?android:attr/borderlessButtonStyle"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_alignParentRight="true"         android:text="@string/btn_save" />  </RelativeLayout>  <LinearLayout     android:id="@+id/lnr_parent"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:layout_below="@+id/rcl_header_btn"     android:gravity="center_vertical"     android:orientation="vertical">      <EditText         android:id="@+id/edt_description"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:hint="DESCRIPTION" />      <EditText         android:id="@+id/edt_address"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:hint="Address" />      <LinearLayout         android:id="@+id/lnr_child_1"         android:layout_width="match_parent"         android:layout_height="wrap_content">          <EditText             android:id="@+id/edt_city"             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:layout_weight="1"             android:hint="City" />          <EditText             android:id="@+id/edt_state"             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:layout_weight="1"             android:hint="State" />      </LinearLayout>      <LinearLayout         android:id="@+id/lnr_child_2"         android:layout_width="match_parent"         android:layout_height="wrap_content">          <EditText             android:id="@+id/edt_zipcode"             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:layout_weight="1"             android:hint="Zip Code" />          <EditText             android:id="@+id/edt_country"             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:layout_weight="1"             android:hint="Country" />      </LinearLayout> </LinearLayout>  <RelativeLayout     android:id="@+id/rcl_bottom"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     android:layout_below="@+id/lnr_parent">      <CheckBox         android:id="@+id/chk_marked"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_alignBaseline="@+id/txt_type" />      <TextView         android:id="@+id/txt_type"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_alignBaseline="@+id/btn_delete"         android:layout_toRightOf="@+id/chk_marked"         android:gravity="center"         android:text="SET AS DEFAULT" />      <Button         android:id="@+id/btn_delete"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_alignParentRight="true"         android:text="DELETE" />  </RelativeLayout> 

5.Adapter

public class PeopleAdapter extends ExpandableRecyclerAdapter<PeopleAdapter.PeopleListItem> { public static final int TYPE_PERSON = 1001;  public PeopleAdapter(Context context) {     super(context);      setItems(getSampleItems()); }  public static class PeopleListItem extends ExpandableRecyclerAdapter.ListItem {     public String Text;      public PeopleListItem(String group) {         super(TYPE_HEADER);          Text = group;     }      public PeopleListItem(String first, String last) {         super(TYPE_PERSON);          Text = first + " " + last;     }     }      public class HeaderViewHolder extends ExpandableRecyclerAdapter.HeaderViewHolder {     TextView name;      public HeaderViewHolder(View view) {         super(view, (ImageView) view.findViewById(R.id.img_arrow));          name = (TextView) view.findViewById(R.id.txt_header_name);     }      public void bind(int position) {         super.bind(position);          name.setText(visibleItems.get(position).Text);     }     }      public class PersonViewHolder extends ExpandableRecyclerAdapter.ViewHolder {     EditText name;      public PersonViewHolder(View view) {         super(view);          name = (EditText) view.findViewById(R.id.edt_description);     }      public void bind(int position) {         name.setText(name.getText());     }      }      @Override     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {     switch (viewType) {         case TYPE_HEADER:             return new HeaderViewHolder(inflate(R.layout.item_header, parent));         case TYPE_PERSON:         default:             return new PersonViewHolder(inflate(R.layout.item_content, parent));     }     }      @Override     public void onBindViewHolder(ExpandableRecyclerAdapter.ViewHolder holder, int position) {     switch (getItemViewType(position)) {         case TYPE_HEADER:             ((HeaderViewHolder) holder).bind(position);             break;         case TYPE_PERSON:         default:             ((PersonViewHolder) holder).bind(position);             break;     }     }      private List<PeopleListItem> getSampleItems() {     List<PeopleListItem> items = new ArrayList<>();     items.add(new PeopleListItem("Friends"));     items.add(new PeopleListItem("", ""));     items.add(new PeopleListItem("Friends"));     items.add(new PeopleListItem("", ""));     return items; } 

}

6.MainActivity.java

public class MainActivity extends AppCompatActivity { RecyclerView recycler; PeopleAdapter adapter;  @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);     setSupportActionBar(toolbar);      recycler = (RecyclerView) findViewById(R.id.main_recycler);      adapter = new PeopleAdapter(this);     adapter.setMode(ExpandableRecyclerAdapter.MODE_ACCORDION);     recycler.setLayoutManager(new LinearLayoutManager(this));     recycler.setAdapter(adapter); }  @Override public boolean onCreateOptionsMenu(Menu menu) {     super.onCreateOptionsMenu(menu);      getMenuInflater().inflate(R.menu.menu_main, menu);      return true; }  @Override public boolean onOptionsItemSelected(MenuItem item) {     switch (item.getItemId()) {         case R.id.action_expand_all:             adapter.expandAll();             return true;         case R.id.action_collapse_all:             adapter.collapseAll();             return true;         default:             return super.onOptionsItemSelected(item);     } } } 
like image 92
Shweta Chauhan Avatar answered Oct 05 '22 23:10

Shweta Chauhan