什么是ConcatAdapter
ConcatAdapter 是 RecyclerView 在 1.2.0版本推出的新功能,其实就是一个可以合并多个适配器的适配器。就是可以将上下两部分不同的列表,组合放到一个recyclerview中。当然也有小机灵鬼可能会说:啊我用NestedScrollView加 两个recyclerview 也可以实现两个列表嵌套滑动啊。确实,实现是能实现,但是NestedScrollView 嵌套 recyclerview 是存在无法复用viewholder的问题的。真的不建议这么做啊。
怎么用ConcatAdapter
使用上没有什么难度。两个列表的话那么自定义的两个adapter 还是少不了的。
eventAdapter = new FragmentEventAdapter(getContext(), viewModel.joyList);
shopAdapter = new EventShopAdapter(getContext(), viewModel.shopList);
ConcatAdapter concatAdapter = new ConcatAdapter( shopAdapter, eventAdapter);
就很简单,将两个adapter塞进去就可以了,当然有多个列表也是可以塞多个的。
注意: 用了ConcatAdapter 之后呢,那么在 ViewHolder 里面就不建议使用 getAdapterPosition 方法来获取当前的下标了。ViewHolder 里面也新增了两个方法 : getBindingAdapterPosition() 和getAbsoluteAdapterPosition()。
getBindingAdapterPosition():获取当前item 在绑定的adapter中的下标。
getAbsoluteAdapterPosition():获取当前item 在整个recyclerview中的下标。
存在的问题
1、如果在adapter 中使用了多viewType 的话,那么默认情况下 ConcatAdapter 会将你的viewType转换为从 0开始的int型整数。
可以从ConcatAdapter 的getItemViewType源码看起:
上面的代码是先通过下标找到对应的adapter的封装类 NestedAdapterWrapper,然后再调它的 getItemViewType。
可以看到这里是先获取了我们自定义的 viewType,然后 调用了 mViewTypeLookup.localToGlobal 方法转换成新的 viewType。那么这个mViewTypeLookup 是怎么来的呢,我们看它在哪赋值的:
viewTypeStorage.createViewTypeWrapper(this); 这个方法产生的,继续看哪里调用了NestedAdapterWrapper 构造函数:
继续看mViewTypeStorage 是怎么来的:
可以看到是根据 config.isolateViewTypes 这个变量来定的 。config 是我们创建 ConcatAdapter 的时候默认给了一个值得。
那么这里就可以看到 config.isolateViewTypes 的值 是ture 了。那么我们回到上面的步骤,先看viewTypeStorage.createViewTypeWrapper 方法:
所以 mViewTypeLookup.localToGlobal 调用的就是 WrapperViewTypeLookup#localToGlobal代码如下:
这里可以看到是 现在缓存的 mLocalToGlobalMapping 里面找,没有则通过 obtainViewType方法创建一个。
到这里就清晰了,获取了我们自定义的 viewType 之后,通过转换为从0 开始的整数。
这里要注意一下,ConcatAdapter 会在调用 自定义adapter#onCreateViewHolder 的时候将你自定义的 viewType 传过去。
那么既然我们都看了 config.isolateViewTypes 为 ture 的情况了,那必然要看下一为 false的情况了:
代码很简单啊,最后就是原样返回了我们的 viewType
所以要解决这个问题也很简单,将 config.isolateViewTypes 变为false 就行了:
这个参数的作用,字面的理解就是: 是否隔壁彼此的试图池。
我们都知道recyclerview 有四重缓存。其中 mCachedViews 默认的缓存池大小是5。我的理解就是 这个参数是 true 的时候,每一个adapter 都独享一个缓存池。
官方的原话是这样的:
By default, each adapter maintains their own pool of ViewHolders, with no re-use in between adapters. If multiple adapters display the same ViewHolder, we may want to reuse instances between them. We can achieve this by constructing our ConcatAdapter with a ConcatAdapter.Config object, where isolateViewTypes = false. Like this, all the adapters merged will use the same view pool. In the loading status header and footer example, both ViewHolders will actually display the same content so we could reuse them.
翻译了一下:
默认情况下,每个适配器都维护自己的 ViewHolders 池,适配器之间不会重复使用。 如果多个适配器显示相同的 ViewHolder,我们可能希望在它们之间重用实例。 我们可以通过使用 ConcatAdapter.Config 对象构造我们的 ConcatAdapter 来实现这一点,其中isolateViewTypes = false。 像这样,所有合并的适配器将使用相同的视图池。 在加载状态页眉和页脚示例中,两个 ViewHolder 实际上将显示相同的内容,以便我们可以重用它们。
官方博文: 链接