【问题标题】:Creating an expandable RecyclerView创建一个可扩展的 RecyclerView
【发布时间】:2015-07-05 09:32:15
【问题描述】:

我正在尝试实现一个 recyclerview,其行为类似于我下面的草图:

这个想法是有一个父列表,当点击父列表中的列表项时,该列表项会显示一个包含它自己的数据的子列表。在子列表中点击列表项时,会反映该子项的值并更新父列表项中父项的值。

在过去的 3 天里,我试图让它工作,但无济于事。我尝试使用AdvancedReyclerview library,但对于像我这样的初学者来说,这是一堆没有意义的东西,尤其是在传递数据时。我复制并粘贴了获得最小工作版本所需的文件,但我不知道如何将我的数据传递到 recyclerview 以及如何使用新选择的值对其进行更新。

是否有可能做我想做的事情,或者我在这里超出了我的深度?

如果还是难以理解,我可以再解释一下。

编辑:有人建议我使用 ExpandableListView 而不是 RecyclerView。对此有什么想法吗?

【问题讨论】:

    标签: android listview android-recyclerview expandablelistview


    【解决方案1】:

    这有点晚了,但是看看这个高级的 recyclerview 库https://github.com/h6ah4i/android-advancedrecyclerview

    在他们的文档中,您可以看到 与可扩展项目相关的类/接口 看看吧。

    【讨论】:

    • 斯巴达人,我检查了他们的示例,但发现很难遵循。我需要在项目中添加什么才能开始使用他们的扩展库?除了编译目录还有什么,我需要将任何lib文件复制到我的项目中吗?
    【解决方案2】:

    你可以在here查看我的图书馆

    并创建如下代码:

    public class PurchaseItemRecyclerViewAdapter extends  ExpandableRecyclerView.Adapter<PurchaseItemRecyclerViewAdapter.ChildViewHolder,ExpandableRecyclerView.SimpleGroupViewHolder,String,String>
    {
    
    List<ItemModel> itemModels;
    
    public PurchaseItemRecyclerViewAdapter() {
        this.itemModels = new ArrayList<>();
        itemModels.add(new ItemModel("group 0",3,"subitem :"));
        itemModels.add(new ItemModel("group 1",3,"subitem :"));
        itemModels.add(new ItemModel("group 2",2,"subitem :"));
        itemModels.add(new ItemModel("group 3",1,"subitem :"));
        itemModels.add(new ItemModel("group 4",3,"subitem :"));
        itemModels.add(new ItemModel("group 5",5,"subitem :"));
    }
    
    @Override
    public int getGroupItemCount() {
        return itemModels.size();
    }
    
    @Override
    public int getChildItemCount(int i) {
        return itemModels.get(i).getSubItemCount();
    }
    
    @Override
    public String getGroupItem(int i) {
        return itemModels.get(i).getParentName();
    }
    
    @Override
    public String getChildItem(int group, int child) {
        return itemModels.get(group).getSubItemPrefix() + child;
    }
    
    @Override
    protected ExpandableRecyclerView.SimpleGroupViewHolder onCreateGroupViewHolder(ViewGroup parent)
    {
        return new ExpandableRecyclerView.SimpleGroupViewHolder(parent.getContext());
    }
    
    @Override
    protected ChildViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType)
    {
        View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.purchase_list_content,parent,false);
        return new ChildViewHolder(rootView);
    }
    
    @Override
    public void onBindGroupViewHolder(ExpandableRecyclerView.SimpleGroupViewHolder holder, int group) {
        super.onBindGroupViewHolder(holder, group);
        holder.setText(getGroupItem(group));
    
    }
    
    @Override
    public void onBindChildViewHolder(ChildViewHolder holder, final int group, int position)
    {
        super.onBindChildViewHolder(holder, group, position);
        holder.name.setText(getChildItem(group, position));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                itemModels.get(group).setParentName("edited Parent");
                notifyItemChanged(group);
            }
        });
    }
    
    @Override
    public int getChildItemViewType(int i, int i1) {
        return 1;
    }
    
    public class ChildViewHolder extends RecyclerView.ViewHolder
    {
        private TextView name;
        public ChildViewHolder(View itemView) {
            super(itemView);
            name = (TextView) itemView.findViewById(R.id.item_name);
        }
    }
    }
    

    还有这个 ItemModel 类:

    public class ItemModel {
    String parentName;
    int subItemCount;
    String subItemPrefix;
    
    public ItemModel(String parentName, int subItemCount, String subItemPrefix) {
        this.parentName = parentName;
        this.subItemCount = subItemCount;
        this.subItemPrefix = subItemPrefix;
    }
    
    public String getParentName() {
        return parentName;
    }
    
    public void setParentName(String parentName) {
        this.parentName = parentName;
    }
    
    public int getSubItemCount() {
        return subItemCount;
    }
    
    public void setSubItemCount(int subItemCount) {
        this.subItemCount = subItemCount;
    }
    
    public String getSubItemPrefix() {
        return subItemPrefix;
    }
    
    public void setSubItemPrefix(String subItemPrefix) {
        this.subItemPrefix = subItemPrefix;
    }
    }
    

    【讨论】:

      【解决方案3】:

      你可以用这个library轻松实现它,有一个完整的例子here

      基本上你将你的项目分成几个部分:

      class MySection extends StatelessSection {
      
          String header;
          List<String> list;
          boolean expanded = true;
      
          public MySection(String header, List<String> list) {
              // call constructor with layout resources for this Section header and items 
              super(R.layout.section_header, R.layout.section_item);
              this.myHeader = header;
              this.myList = list;
          }
      
          @Override
          public int getContentItemsTotal() {
              return expanded? list.size() : 0;
          }
      
          @Override
          public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
              return new HeaderViewHolder(view);
          }
      
          @Override
          public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
              final HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
      
              headerHolder.tvTitle.setText(title);
      
              headerHolder.rootView.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      expanded = !expanded;
                      headerHolder.imgArrow.setImageResource(
                              expanded ? R.drawable.ic_keyboard_arrow_up_black_18dp : R.drawable.ic_keyboard_arrow_down_black_18dp
                      );
                      sectionAdapter.notifyDataSetChanged();
                  }
              });
          }
      
          @Override
          public RecyclerView.ViewHolder getItemViewHolder(View view) {
              // return a custom instance of ViewHolder for the items of this section
              return new MyItemViewHolder(view);
          }
      
          @Override
          public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
              MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
      
              // bind your view here
              itemHolder.tvItem.setText(list.get(position));
          }
      }
      

      然后创建您的部分实例并设置您的适配器:

      // Create an instance of SectionedRecyclerViewAdapter 
      SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
      
      // Add your Sections
      sectionAdapter.addSection(new MySection("A", Arrays.asList(new String[] {"a", "b", "c" })));
      sectionAdapter.addSection(new MySection("B", Arrays.asList(new String[] {"d", "e", "f" })));
      
      // Set up your RecyclerView with the SectionedRecyclerViewAdapter
      RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
      recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
      recyclerView.setAdapter(sectionAdapter);
      

      【讨论】:

        【解决方案4】:

        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.适配器

        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);
            }
        }
        }
        

        【讨论】:

        • 能否分享您的代码项目或 Git !
        • @Umasankar :我认为整个代码都在这里。有什么不懂的告诉我,我会分享给你的
        • 你好 Shweta,我得到了未知的 ID,例如 R.id.toolbar、R.id.action_expand_all、R.id.action_collapse_all,这里我需要菜单文件 (menu_mail) 和有关上述 ID 的资源文件
        • 如果你更新上面的结果截图,它可能看起来很酷!
        • 谢谢!它按预期工作
        猜你喜欢
        • 2017-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多