最近有RecyclerView点击展开、折叠item的需求,其中一种写法是利用两个RecyclerView实现,但这样效率不高。因此采用单层RecyclerView来实现此需求。
1 定义两个布局item的结构,Adapter的数据源为两种item组装成的集合
1.1 子item:ChildBookItem
简单起见,只展示一个bookName
private String bookName;
1.2 父item:CategoryItem
privateint index;privateboolean isShow;// 展开状态private String categoryName;private List<ChildBookItem> childBookItems;// 子item列表
index表示在item集合中此CategoryItem的位置(不算ChildBookItem的数量);
List childBookItems 是此Category在展开状态下所包含的子item集合,只用于记录对应关系,并不参与绘制UI。
2 组装数据
2.1 获取数据源
private List<BookShelf>initData(){
BookShelf bookShelf1=newBookShelf();
bookShelf1.setName("科幻类");
String[] books1=newString[]{"三体","流浪地球","降临"};
bookShelf1.setBooks(Arrays.asList(books1));
mDatas.add(bookShelf1);
BookShelf bookShelf2=newBookShelf();
bookShelf2.setName("政治类");
String[] books2=newString[]{"美国陷阱","从赫鲁晓夫到普京","为什么是以色列","南京大屠杀"};
bookShelf2.setBooks(Arrays.asList(books2));
mDatas.add(bookShelf2);
BookShelf bookShelf3=newBookShelf();
bookShelf3.setName("文学类");
String[] books3=newString[]{"我们仨","小姨多鹤","我与地坛","黄金时代","雪国"};
bookShelf3.setBooks(Arrays.asList(books3));
mDatas.add(bookShelf3);return mDatas;}
2.2 初始化组装item
由于初始不展示子item,所以不需要设置ChildBookItems的内容。此时定义的Adapter绑定的集合类型为Object,既包含CategoryItem,也包括ChildBookItem,所以不指定具体类型。
private List<Object> mList=newArrayList<>();// item组装数据private List<CategoryItem>initItem(){
List<BookShelf> bookShelves=initData();
List<CategoryItem> items=newArrayList<>();for(int i=0; i< bookShelves.size(); i++){
BookShelf bookShelf= bookShelves.get(i);
CategoryItem categoryItem=newCategoryItem();
categoryItem.setIndex(i);
categoryItem.setShow(false);
categoryItem.setCategoryName(bookShelf.getName());
categoryItem.setChildBookItems(newArrayList<ChildBookItem>());
items.add(categoryItem);}return items;}
此时的items包含了三条数据,均为父item类型
2.3 根据item的不同,在Adapter中调用不同的ViewHolder
BookViewHolder与CategoryViewHolder均为继承BaseViewHolder的ViewHolder,可在两个文件中分别进行item相关UI展示。
下边为Adapter部分关键代码:
public BaseViewHolderonCreateViewHolder(@NonNull ViewGroup viewGroup,int i){switch(i){case TYPE_CATEGORY:returnnewCategoryViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_category_item, viewGroup,false),this);case TYPE_BOOK:returnnewBookViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_item_book, viewGroup,false));default:returnnewBaseViewHolder(newLinearLayout(viewGroup.getContext())){@OverridepublicvoidbindView(List list, Object obj){}};}}@OverridepublicintgetItemViewType(int position){
Object obj= mDatas.get(position);if(objinstanceofCategoryItem){return TYPE_CATEGORY;}elseif(objinstanceofChildBookItem){return TYPE_BOOK;}return-1;}@OverridepublicvoidonBindViewHolder(@NonNull BaseViewHolder baseViewHolder,int i){
baseViewHolder.bindView(mDatas, mDatas.get(i));}
3 点击操作
3.1 展开子item
mList.addAll(mSelectedPosition + 1, childBookItems):将ChildBookItem集合加入到适配器绑定数据集合中,位置在刚刚点击的父item后一位;
mSelectedPosition:为刚刚点击的父item的position;
categoryItem.setChildBookItems(childBookItems):确定父item与子item集合的对应关系,方便后边折叠item时候直接从mList中删除此集合。
// 展开子itemprivatevoidaddChildItems(int index){
List<String> bookList= mDatas.get(index).getBooks();
List<ChildBookItem> childBookItems=newArrayList<>();for(int i=0; i< bookList.size(); i++){
ChildBookItem item=newChildBookItem();
item.setBookName(bookList.get(i));
childBookItems.add(item);}
mList.addAll(mSelectedPosition+1, childBookItems);// 添加子item
CategoryItem categoryItem=(CategoryItem) mList.get(mSelectedPosition);
categoryItem.setChildBookItems(childBookItems);
mAdapter.notifyDataSetChanged();}
若此时点击最后一个父item,可看到mList中前三条数据为CategoryItem类型;后五条数据为ChildBookItem类型,并与第三条CategoryItem的子item列表内容相同。
3.2 折叠子item
mList.removeAll(item.getChildBookItems()): 由于之前展开item的时候已经设置好了CategoryItem与ChildBookItem之间的对应关系,所以可通过此行代码直接从mList中删除子item。
privatevoidremoveChildItems(){
CategoryItem item=(CategoryItem)mList.get(mSelectedPosition);
mList.removeAll(item.getChildBookItems());
item.setChildBookItems(newArrayList<ChildBookItem>());
mAdapter.notifyDataSetChanged();}