当前位置:   article > 正文

聊聊RecyclerView新出的ConcatAdapter如何使用

concatadapter recyclerview

1.前言

2021年4月7日Android团队正式发布了RecyclerView 1.2.0版本。相对于1.1.0版本,它有两个主要的变化:

  1. 增加了ConcatAdapter:这个Adapter方便地让我们在一个RecyclerView中连接多个Adapters。

  2. 支持延迟恢复状态:RecyclerView现在支持当内容加载出来后恢复状态。

本文将结合ConcatAdapter的简单使用,由浅入深地讲解ConcatAdapter的高级使用。

2.简单使用

实现上面是文本列表,下面是按钮列表的效果,如图:

运行效果

2.1 不使用ConcatAdapter实现

在RecyclerView 1.2.0之前,我们可以通过Adapte的getItemViewType方法,设置文本和按钮两种类型。来完成上述效果。伪代码如下,通过TEXT_TYPE和BUTTON_TYPE两种类型,创建不同的视图。

2.2 使用ConcatAdapter实现

使用ConcatAdapter实现该效果。只需要创建TextAdapter处理文本列表,创建ButtonAdapter处理按钮列表。通过ConcatAdapter将它们串联起来。代码如下:

2.3 优势和劣势

使用ConcatAdapter的优势是Adapter可重用性高,更专注在业务上,不必考虑各种不同ItemType的场景,耦合度低。劣势是,ConcatAdapter不支持不同ItemType交叉出现的场景。

3. 高级进阶

以上就是ConcatAdapter简单使用的全部教程。但是如果你认为ConcatAdapter就这么简单那你就大错特错了。让我们深入源码,玩点更高级的特性吧。

3.1 Config类

我们看到ConcatAdapter有如下构造函数。我们注意到Config类是ConcatAdapter的静态内部类。

  1. public ConcatAdapter(Adapter<? extends ViewHolder>... adapters) {
  2.     this(Config.DEFAULT, adapters);
  3. }
  4. public ConcatAdapter(Config config, Adapter<? extends ViewHolder>... adapters) {
  5.     this(config, Arrays.asList(adapters));
  6. }

Config构造函数如下:

  1. Config(boolean isolateViewTypes, StableIdMode stableIdMode) {
  2.     this.isolateViewTypes = isolateViewTypes;
  3.     this.stableIdMode = stableIdMode;
  4. }
public static final Config DEFAULT = new Config(true, NO_STABLE_IDS);

我们注意到默认的Config,isolateViewTypes值为true。

3.2 isolateViewTypes含义

要讲清楚isolateViewTypes的含义,那么必须先明白viewType与缓存的关系。我们都知道RecyclerViewPool中是根据viewType缓存ViewHolder的。如果viewType相同,那么它对应的缓存池相同。

RecyclerViewPool缓存示意图如下。每个不同的viewType都有一个属于它自己的缓存。

isolateViewTypes为true。表示ConcatAdapter中的子Adapter的viewType,会被ConcatAdapter隔离开。即使两个子Adapter的中元素的viewType相同,ConcatAdapter会将它们分隔成不同的viewType。从缓存的角度看,即使两个相同的Adapter,它们也无法共用一个缓存池。

isolateViewTypes为false。表示如果viewType相同,那么它们将共用一个缓存池。

3.1 不共用缓存

假设有ConcatAdapter,连接了RedAdapter、OrangeAdapter、BlueAdapter、RedAdapter。使用默认Config。isolateViewTypes为true,我们看到RedAdapter的ViewType默认返回1。但是从ConcatAdapter的角度。两个RedAdapter的viewType分别为0和3。

  1. RedAdapter redAdapter1 = xxx;
  2. OrangeAdapter orangerAdapter = xxx;
  3. BlueAdapter blueAdapter = xxx;
  4. RedAdapter redAdapter2 = xxx;
  5. ConcatAdapter concatenated = new ConcatAdapter(redAdapter1, orangerAdapter,blueAdapter,redAdapter2);
  6. recyclerView.setAdapter(concatenated);

3.2 共用缓存

  1. RedAdapter redAdapter1 = xxx;
  2. OrangeAdapter orangerAdapter = xxx;
  3. BlueAdapter blueAdapter = xxx;
  4. RedAdapter redAdapter2 = xxx;
  5. //isolateViewTypes为false
  6. ConcatAdapter.Config config = ConcatAdapter.Config.Builder().setIsolateViewTypes(false).build()
  7. ConcatAdapter concatenated = new ConcatAdapter(config, redAdapter1, orangerAdapter,blueAdapter,redAdapter2);
  8. recyclerView.setAdapter(concatenated);

ConcatAdapter的itemType 和子Adapter的itemType一致。

4. 爬坑

4.1 坑一

子Adapter的getItemViewType返回默认值。ConcatAdapter isolateViewType设置为false。

TextAdapter和ButtonAdapter和上文一样。

  1. class ConcatAdapterDemoActivity : AppCompatActivity() {
  2.     override fun onCreate(savedInstanceState: Bundle?) {
  3.         super.onCreate(savedInstanceState)
  4.         setContentView(R.layout.activity_concat_adapter_demo)
  5.         val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)
  6.         recyclerView.layoutManager = LinearLayoutManager(this)
  7.         val config = ConcatAdapter.Config.Builder().setIsolateViewTypes(false).build()
  8.         recyclerView.adapter = ConcatAdapter(
  9.             config,
  10.             TextAdapter(),
  11.             ButtonAdapter()
  12.         )
  13.     }
  14. }

4.2 坑二

ConcatAdapter连接多个TextAdapter。isolateViewType设置为true。发现滚动到第二个TextAdapter位置时,又创建了新的TextView。这种情况,相同viewType需要共用缓存。将isolateViewType设置为false。

  1. recyclerView.adapter = ConcatAdapter(
  2.       config,
  3.       TextAdapter(),
  4.       ButtonAdapter(),
  5.       TextAdapter()
  6. )

5. 原理

  1. ConcatAdapter.getItemViewType()

  1. //ConcatAdapter.java
  2. @Override
  3. public int getItemViewType(int position) {
  4.     return mController.getItemViewType(position);
  5. }
  1. ConcatAdapterController.getItemViewType(int globalPosition)

  1. //ConcatAdapterController.java
  2. public int getItemViewType(int globalPosition) {
  3.       //根据globalPosition找到对应的子Adapter的Wrapper
  4.       WrapperAndLocalPosition wrapperAndPos = findWrapperAndLocalPosition(globalPosition);
  5.       int itemViewType = wrapperAndPos.mWrapper.getItemViewType(wrapperAndPos.mLocalPosition);
  6.       releaseWrapperAndLocalPosition(wrapperAndPos);
  7.       return itemViewType;
  8. }
  1. NestedAdapterWrapper.getItemViewType(int localPosition)

  1. int getItemViewType(int localPosition) {
  2.     return mViewTypeLookup.localToGlobal(adapter.getItemViewType(localPosition));
  3. }
  1. IsolatedViewTypeStorage$WrapperViewTypeLookup.localToGlobal()

  1. @Override
  2. public int localToGlobal(int localType) {
  3.     int index = mLocalToGlobalMapping.indexOfKey(localType);
  4.     if (index > -1) {
  5.         return mLocalToGlobalMapping.valueAt(index);
  6.     }
  7.     // get a new key.
  8.     int globalType = obtainViewType(mWrapper);
  9.     mLocalToGlobalMapping.put(localType, globalType);
  10.     mGlobalToLocalMapping.put(globalType, localType);
  11.     return globalType;
  12. }
  1. IsolatedViewTypeStorage$WrapperViewTypeLookup。从代码可以看出在不共享缓存池的情况下。「子Adapter的viewType会从0递增对应」

  1. int mNextViewType = 0;
  2. int obtainViewType(NestedAdapterWrapper wrapper) {
  3.     int nextId = mNextViewType++;
  4.     mGlobalTypeToWrapper.put(nextId, wrapper);
  5.     return nextId;
  6. }
  1. isolateViewTypes为true的情况下。会使用SharedIdRangeViewTypeStorage$WrapperViewTypeLookup。我们看到 localType和globalType相等。

  1. @Override
  2. public int localToGlobal(int localType) {
  3.     // register it first
  4.     List<NestedAdapterWrapper> wrappers = mGlobalTypeToWrapper.get(
  5.             localType);
  6.     if (wrappers == null) {
  7.         wrappers = new ArrayList<>();
  8.         mGlobalTypeToWrapper.put(localType, wrappers);
  9.     }
  10.     if (!wrappers.contains(mWrapper)) {
  11.         wrappers.add(mWrapper);
  12.     }
  13.     return localType;
  14. }
  15. @Override
  16. public int globalToLocal(int globalType) {
  17.     return globalType;
  18. }

6. 最后

成文仓促,如果有表述不清晰的地方,欢迎留言探讨。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/286587
推荐阅读
相关标签
  

闽ICP备14008679号