Android新发布的ConcatAdapter使用教程

简介: Android新发布的ConcatAdapter使用教程

1.前言



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


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


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

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


2.简单使用



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


image.png


image.png

2.1 不使用ConcatAdapter实现


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


image.png

2.2 使用ConcatAdapter实现


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


image.png

image.png

image.png

2.3 优势和劣势


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


3. 高级进阶


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


3.1 Config类


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


public ConcatAdapter(Adapter<? extends ViewHolder>... adapters) {
    this(Config.DEFAULT, adapters);
}
public ConcatAdapter(Config config, Adapter<? extends ViewHolder>... adapters) {
    this(config, Arrays.asList(adapters));
}


Config构造函数如下:


Config(boolean isolateViewTypes, StableIdMode stableIdMode) {
    this.isolateViewTypes = isolateViewTypes;
    this.stableIdMode = stableIdMode;
}
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都有一个属于它自己的缓存。


image.png

image.png

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。


RedAdapter redAdapter1 = xxx;
OrangeAdapter orangerAdapter = xxx;
BlueAdapter blueAdapter = xxx;
RedAdapter redAdapter2 = xxx;
ConcatAdapter concatenated = new ConcatAdapter(redAdapter1, orangerAdapter,blueAdapter,redAdapter2);
recyclerView.setAdapter(concatenated);


image.png


3.2 共用缓存


RedAdapter redAdapter1 = xxx;
OrangeAdapter orangerAdapter = xxx;
BlueAdapter blueAdapter = xxx;
RedAdapter redAdapter2 = xxx;
//isolateViewTypes为false
ConcatAdapter.Config config = ConcatAdapter.Config.Builder().setIsolateViewTypes(false).build()
ConcatAdapter concatenated = new ConcatAdapter(config, redAdapter1, orangerAdapter,blueAdapter,redAdapter2);
recyclerView.setAdapter(concatenated);


ConcatAdapter的itemType 和子Adapter的itemType一致。

image.png

5. 原理



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


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


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


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


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


@Override
public int localToGlobal(int localType) {
    // register it first
    List<NestedAdapterWrapper> wrappers = mGlobalTypeToWrapper.get(
            localType);
    if (wrappers == null) {
        wrappers = new ArrayList<>();
        mGlobalTypeToWrapper.put(localType, wrappers);
    }
    if (!wrappers.contains(mWrapper)) {
        wrappers.add(mWrapper);
    }
    return localType;
}
@Override
public int globalToLocal(int globalType) {
    return globalType;
}


相关文章
|
6月前
|
网络协议 Android开发 数据安全/隐私保护
Android手机上使用Socks5全局代理-教程+软件
Android手机上使用Socks5全局代理-教程+软件
4935 2
|
6月前
|
缓存 网络协议 安全
49. 【Android教程】HTTP 使用详解
49. 【Android教程】HTTP 使用详解
93 1
|
27天前
|
Android开发
布谷语音软件开发:android端语音软件搭建开发教程
语音软件搭建android端语音软件开发教程!
|
6月前
|
XML 存储 JSON
51. 【Android教程】JSON 数据解析
51. 【Android教程】JSON 数据解析
153 2
|
6月前
|
存储 API 文件存储
47. 【Android教程】SharedPreferences 存储
47. 【Android教程】SharedPreferences 存储
69 2
|
6月前
|
调度 Android开发
43. 【Android教程】服务:Service
43. 【Android教程】服务:Service
61 2
|
6月前
|
存储 安全 大数据
46. 【Android教程】文件存储
46. 【Android教程】文件存储
65 3
|
6月前
|
存储 编解码 Android开发
58. 【Android教程】音频录制:MediaRecord
58. 【Android教程】音频录制:MediaRecord
65 2
|
6月前
|
设计模式 Android开发
44. 【Android教程】广播接收器:Broadcast Receiver
44. 【Android教程】广播接收器:Broadcast Receiver
129 2
|
6月前
|
安全 数据库 Android开发
45. 【Android教程】内容提供者 - Content Provider
45. 【Android教程】内容提供者 - Content Provider
77 2