前言:
根据ExpandableListView的原理实现的ExpandableRecyclerView,并根据RecyclerView的刷新机制增加Group,Child的相关刷新和查询功能, 并在其基础上加入PositionDatadata缓存
RecyclerViw之ExpandableRecyclerView
RecyclerViw之StickyRecyclerView
RecyclerView之WheelView
RecyclerView之BannerPager
先看功能图和效果图
测试demo.apk包下载
一:实现原理
在查找adapterPosition对应的Group或者Child的groupPosition与childPosition基本与ExpandableListView一致
1. 看过ExpandableListView原码的应该都清楚, 在实现Expandable中主要逻辑就是要根据当前adapterPosition来找出对应的是Group还是Child, 而网上大多实现方式类似将每个组当成一个List, 再将整个Adapter当成多个List, 或者是将Group封装成Position, 在根据adapterPosition查找Group或Child时遍历, 这里分析下有哪些弊端
A:
查找Group或者Child , 既然是封装的List或者是Position在查找的时候, 这些列表所对应的adapterPosition已经相当是排序过的, 那么一定要用二分查找
B:
封装成List需要传入List,getItmCount会大量调用,每次都要计算itemCount
C.
在Adapter中getItemCount和getItemViewType方法会大量的调用, 而这些方法中又需要计算 getItemCount和getItemViewType, 在onBindViewHolder时也需要计算, 特别是在GridLayoutManager或者StaggeredGridLayoutManager布局时getItemCount和getItemViewType会大量重复调用
D.
在刷新时要加入单个组, 或者加入range组时, 大多都只能notifyDataSetChanged, 这个方法早已经被google警告了,建议使用其它的刷新机制性能会更忧如notifyItemRangeChanged, notifyItemRangeInserted,notifyItemRangeRemoved等
E.
在展开或者叠起时或者实际开发也会需要查找adapterPosition来获取Child或者Group信息, 或者通过groupPosition,childPosition查找adapterPosition这里又会大量的循环查找性能较差
F.
ExpandableListView的实现方式, ExpandableListView是将组信息封装成GroupMetada, 每一个要查找的position封装成PositionDatadata, 保存着adapterPositoin和groupPosition或者childPosition, 只保留展开的GroupMedata, 但是这种方式也只局限了LinearLayoutManager, 在Grid和StaggeredGrid时大量的调用getItemCount和getItemView时性能不好,因此需要加入position缓存
2. 缓存原理
这里的缓存不是LruCache, LinkedHashMap底层原理还是要通过hash & 操作查找到数组索引再链表或者红黑树遍历查找, 这里只根据adapterPosition & 操作找出数组索引位置中的缓存PositionMetadata, 在getItemViewType或者onBindViewHolder新的position
时直接复用PositionMetadata覆盖新的信息, 重复调用时直接使用,可以防止getItemViewType大量重复调用时的性能问题
3. 扩展
提供PositionMetadata所对应的GroupInfo和ChildInfo类, 在ExpandableRecyclerView中提供查找的方法并也是通过缓存的方式优先查找, 先重复利用的查找方式, 提供groupPosition和childPosition查找对应adaposition, 扩展如StickyRecyclerView
二 使用, 没有过度的封装, 支持ConcatAdapter,Header,Footer
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.youxiaochen:expandable-recyclerview:1.0.4'
}
源码地址 https://github.com/youxiaochen/expandable-recyclerview
ExpandableRecyclerView相关方法
ExpandableRecyclerView 常用方法 | 说明 |
---|---|
expandGroup(int), isGroupExpanded(int) | 组相关操作 展开, 是否展开 |
collapseGroup(int) | 组相关操作 叠起, 是否叠起 |
setAdapter(ExpandableAdapter...) | 适配器,支持ConcatAdapter |
findGroupInfoByIndex(int),findGroupInfoByIndex(int,GroupInfo) | 通过groupPos查找相GroupInfo, GroupInfo查找复用 |
findChildInfoByIndex(int,int),findChildInfoByIndex(int,int,ChildInfo) | 通过groupPos,childPos查找ChildInfo, ChildInfo复用 |
findGroupInfoByPosition(int), findChildInfoByPosition(int) | 通过Adapter position查找相关信息,可以是ConcatAdapter 支持info复用 |
findGroupInfoByBindingPosition(int), findChildInfoByBindingPosition(int) | 通过ExpandableAdapter中的位置查找相关信息 ,info复用 |
static isGroupViewType(int) | viewType类型是否为组 |
getCurrentTotalChildCount() | 获取展开的所有Child数量 |
isGroupTypeByPosition, isGroupTypeByBindingPosition | viewType类型是否为组 |
getHeaderCount(), getFooterCount | 获取Header,Footer数量 |
ExpandableAdapter相关方法
ExpandableAdapter 常用方法 | 说明 |
---|---|
getGroupCount, getChildCount | adapter count |
getGroupViewType , getChildViewType | group, child viewType |
onCreateGroupViewHolder, onCreateChildViewHolder | group,child create ViewHolder |
onBindGroupViewHolder, onBindChildViewHolder | 支持 List<Object> payloads |
getGroupItemId, getChildItemId ,hasStableIds | setHasTableIds |
groupCanClick | 组不可点击时全部展开, |
saveExpandableState | 是否保存Expandable状态,关联onSaveStateInstance |
getPositionPoolSize | PositionMetadata缓存数量 |
onGroupStateChanged | 展开或者叠时回调 |
onGroupViewRecycled, onChildViewRecycled | Group,child ViewHolder recycler |
onGroupViewAttachedToWindow, detached | Group AttachedToWindow, detached |
onChildViewAttachedToWindow, detached | Child AttachedToWindow, detached |
onAttachedToExpandableRecyclerView, detached | AttachedToExpandableRecyclerView, detached |
notifyDataSetChanged | adapter 刷新 |
notifyGroupChanged, notifyGroupRangeChanged... | 刷新Group是否连带刷新Child RangeChanged |
notifyGroupInserted, notifyGroupRangeInserted | Group insert, 顺insert时Child是否展开 |
notifyGroupRemoved, notifyGroupRangeRemoved | Group remove rangeRemoved |
notifyChildChanged, notifyChildRangeChanged | Child 刷新 RangeChanged |
notifyChildInserted, notifyChildRangeInserted... | Child inserted RangeInserted |
notifyChildRemoved, notifyChildRangeRemoved | child remove RangeRemoved |
registerAdapterDataObserver,unRegister... | ExpandableAdapter注册与取消 观察者, 用于扩展(如StickyAdapter) |