设备功能适配与数据层
本页面向 btsmart 示例应用(PiHome V1.13.0 / SDK V4.2.0)中的数据层(data 包),重点解析设备功能适配器的实现机制:com.jieli.btsmart.data.adapter 下 20+ 个 RecyclerView 适配器、数据模型(model)与监听器(listeners),以及它们如何通过 BRVAH(BaseQuickAdapter)、Glide、DataBinding 与杰理蓝牙 SDK(RCSPController、DeviceStatusManager)协同工作,把设备功能、设备列表、设置项等数据渲染到界面。
Purpose and Scope
本页覆盖的内容:
data/adapter包中适配器的共同基座(BaseQuickAdapter封装)与典型实现(FunctionAdapter、DeviceListAdapter、CommonListAdapter等)。- 数据模型(
SettingsItem、HistoryDevice)与监听器接口(OnDeleteItemListener、OnLocationItemListener)在适配器中的角色。 BindingUtil中面向 DataBinding 的自定义@BindingAdapter(含列表数据注入setRecycleViewData)。- 适配器与杰理蓝牙 SDK、Glide 之间的数据流与设备状态机映射。
不涉及的兄弟主题(各有独立页面):蓝牙协议与 RCSP 命令封装、UI 页面(ui.* 包中的 Activity/Fragment)、tool 包中的业务工具(如 ProductUtil、AppUtil)、OTA 固件升级流程本身。本页只在必要时提及这些依赖以说明数据流向。
Overview
btsmart 示例应用采用经典的 MVC/MVP 分层:ui 包中的页面(如设备连接页、功能设置页、闹钟设置页)通过 Presenter(如 IDeviceConnectContract.IDeviceConnectPresenter)获取数据,再交给 data.adapter 包中的适配器渲染到 RecyclerView。
数据层 com.jieli.btsmart.data 按职责分为三个子包:
| 子包 | 职责 | 代表类型 |
|---|---|---|
adapter | 列表渲染适配器,全部继承 BRVAH 的 BaseQuickAdapter<T, BaseViewHolder> | DeviceListAdapter、FunctionAdapter、CommonListAdapter |
model | 列表项的纯数据模型 | model.settings.SettingsItem、model.device.HistoryDevice |
listeners | 列表项交互回调接口 | OnDeleteItemListener、OnLocationItemListener |
“设备功能适配”体现在两个层面:
- 功能列表适配:
FunctionAdapter、FunctionListAdapter、FuncSettingsAdapter等把抽象的SettingsItem(名称、值、图标)绑定到具体布局,实现同一份功能数据在不同设备/页面的差异化展示。 - 设备形态适配:
DeviceListAdapter根据设备型号(充电仓待机/工作、左右耳连接/待机、产品 Logo 等ProductModel常量)与连接状态(断开/已连接/使用中/连接中/强制升级)动态渲染不同外观,适配多形态 TWS/音箱/穿戴设备。
Architecture
flowchart TD
subgraph sg_UI["UI 层 (ui.* 包)"]
UI["设备连接页 / 功能设置页 / 闹钟页"]
Presenter["Presenter 接口<br/>(IDeviceConnectContract 等)"]
end
subgraph sg_Data["数据层 com.jieli.btsmart.data"]
subgraph sg_Adapter["adapter 包"]
FA["FunctionAdapter"]
DLA["DeviceListAdapter"]
CLA["CommonListAdapter"]
Other["其他 17+ 适配器"]
end
subgraph sg_Model["model 包"]
SI["SettingsItem"]
HD["HistoryDevice"]
end
subgraph sg_Lsn["listeners 包"]
L1["OnDeleteItemListener"]
L2["OnLocationItemListener"]
end
end
subgraph sg_Frame["框架/第三方依赖"]
BRVAH["BaseQuickAdapter (BRVAH)"]
Glide["Glide 图片加载"]
DB["DataBinding + BindingUtil"]
end
subgraph sg_SDK["杰理蓝牙 SDK / jl_http"]
RCSP["RCSPController"]
DSM["DeviceStatusManager"]
BU["BluetoothUtil"]
PM["ProductModel 常量"]
end
UI --> Presenter
Presenter -->|"setAdapter / setNewInstance"| FA
Presenter -->|"setAdapter / setNewInstance"| DLA
Presenter -->|"setAdapter"| Other
FA --> SI
DLA --> HD
DLA --> L1
DLA --> L2
FA --> BRVAH
DLA --> BRVAH
CLA --> BRVAH
Other --> BRVAH
DLA --> RCSP
DLA --> DSM
DLA --> BU
DLA --> PM
DLA --> Glide
UI --> DB
架构说明:
- UI 层只面向 Presenter 与适配器,不直接操作蓝牙协议;数据层适配器是 UI 与数据之间的“翻译器”。
- **BRVAH(
com.chad.library.adapter.base)**是所有适配器的共同基座,提供setNewInstance、getData、notifyDataSetChanged、getItemPosition等列表操作能力,以及convert()钩子用于逐项绑定视图。 - **设备状态相关适配器(
DeviceListAdapter)**直接依赖杰理蓝牙 SDK 的RCSPController(单例,负责设备连接管理)、DeviceStatusManager(设备使用状态查询)、BluetoothUtil(蓝牙工具)与jl_http的ProductModel常量,这是“设备功能适配”的核心数据来源。 BindingUtil通过@BindingAdapter把 DataBinding 表达式与视图操作解耦,其中setRecycleViewData允许在 XML 中直接注入列表数据到已绑定的BaseQuickAdapter。
数据层包结构总览
data/adapter 包共包含 20+ 个适配器,按业务领域可归类如下(均为 BaseQuickAdapter 子类):
| 业务领域 | 适配器 | 数据模型/用途 |
|---|---|---|
| 设备列表 | DeviceListAdapter、DeviceListAdapterModify、HistoryBtDeviceAdapter、DualDevAdapter | HistoryDevice;设备历史/双设备/编辑模式 |
| 功能设置 | FunctionAdapter、FunctionListAdapter、FuncSettingsAdapter、AppSettingsAdapter、LedSettingsAdapter、HeadsetKeyAdapter | SettingsItem;按键、LED、App 设置 |
| 闹钟 | AlarmAdapter、AlarmRepeatAdapter、AlarmDefaultBellAdapter、BellFileListAdapter | 闹钟列表、重复规则、默认铃声、铃声文件 |
| 文件管理 | FileListAdapter、FileRouterAdapter | 设备文件浏览与路由跳转 |
| OTA/固件 | FirmwareOtaAdapter | OTA 固件选择 |
| 其他 | FittingHistoryAdapter、FMFreqCollectAdapter、CommonListAdapter | 佩戴历史、FM 频率采集、通用设置项 |
适配器统一采用“构造时传入布局 →
convert()中按模型字段绑定视图”的模式。CommonListAdapter是通用型设置列表(标题 + 值 + 分割线),FunctionAdapter是按键功能设置列表,DeviceListAdapter是最复杂的设备卡片列表。
(继续阅读:下一节深入剖析各核心适配器的实现与控制流。)
核心适配器实现剖析
适配器基座:BaseQuickAdapter 封装
数据层所有适配器都继承 com.chad.library.adapter.base.BaseQuickAdapter<T, BaseViewHolder>(BRVAH 库)。该基类封装了 RecyclerView.Adapter 的样板代码:
- 数据管理:
setNewInstance(List<T>)全量替换数据、getData()获取当前列表、addData()/remove()增删。 - 视图复用:
convert(BaseViewHolder helper, T item)是唯一必须实现的抽象方法,每次条目绑定视图时回调。 - 常用辅助:
getItemPosition(item)定位条目位置、getItemCount()获取数量、notifyDataSetChanged()刷新。
BRVAH 的 BaseViewHolder 提供了链式绑定方法(setText、setImageResource、getView、setVisible 等),使适配器代码保持简短且类型安全。选择 BRVAH 而非原生 RecyclerView.Adapter 的设计意图是:20+ 个适配器共享同一套数据操作 API,减少重复代码,并让 UI 层可以统一通过 setNewInstance 注入数据。
FunctionAdapter:按键功能设置适配
FunctionAdapter 是“设备功能适配”最直接的体现——把 SettingsItem(按键名称、当前值、图标资源)渲染到 item_key_settings_two 布局:
public class FunctionAdapter extends BaseQuickAdapter<SettingsItem, BaseViewHolder> {
public FunctionAdapter() {
super(R.layout.item_key_settings_two);
}
@Override
protected void convert(@NonNull BaseViewHolder helper, SettingsItem item) {
if (item != null) {
helper.setImageResource(R.id.iv_key_settings_two_img, item.getResId());
helper.setText(R.id.tv_key_settings_two_key, item.getName());
helper.setText(R.id.tv_key_settings_two_value, item.getValue());
ImageView imageView = helper.getView(R.id.iv_key_settings_two_icon);
imageView.setVisibility(item.isShowIcon() ? View.VISIBLE : View.GONE);
}
}
}
设计要点:
- 构造时直接传入布局 ID,不接收初始数据,数据由外部随后
setNewInstance注入。 convert()中先判空(item != null)再绑定,避免 RecyclerView 复用时的空指针。- 图标显隐由模型自描述:
SettingsItem.isShowIcon()控制iv_key_settings_two_icon,把“是否显示图标”的决策下沉到数据模型,适配器只做机械绑定——这是适配器与业务解耦的典型手法。 - 被注释的分割线逻辑(
view_key_settings_two_line)表明曾经支持按条目位置隐藏末行分割线,该能力最终由CommonListAdapter显式实现。
CommonListAdapter:通用设置列表
CommonListAdapter 是通用型设置列表适配器,核心是内部类 ValueType 定义的值类型枚举(IMAGE = 0、TEXT = 1):
public final class CommonListAdapter extends BaseQuickAdapter<SettingsItem, BaseViewHolder> {
public final static class ValueType {
public static final int IMAGE = 0;
public static final int TEXT = 1;
}
public CommonListAdapter(List<SettingsItem> list) {
super(R.layout.item_common_list, list);
}
@Override
protected void convert(@NonNull BaseViewHolder helper, SettingsItem item) {
helper.setText(R.id.item_title, item.getName());
int position = getItemPosition(item);
if (getItemCount() > 0 && position == getItemCount() - 1) {//不显示最后一条
helper.getView(R.id.item_divider).setVisibility(View.GONE);
} else {
helper.getView(R.id.item_divider).setVisibility(View.VISIBLE);
}
switch (item.getType()) {
case ValueType.IMAGE:
helper.setVisible(R.id.item_value, false);
default:
break;
case ValueType.TEXT:
TextView tvText = helper.getView(R.id.item_value);
tvText.setText(item.getValue());
helper.setVisible(R.id.item_next_logo, false);
break;
}
}
}
设计要点:
- 通过
SettingsItem.getType()分支渲染“图片值”或“文本值”两种行样式,同一布局复用两种语义。 - 最后一条隐藏分割线(
position == getItemCount() - 1),是列表视觉规范在适配器层的落实;getItemPosition(item)由 BRVAH 提供,无需手写线性查找。 switch中IMAGE分支只有setVisible(R.id.item_value, false)后break落在default上,语法上利用 fall-through 保证 IMAGE 类型不显示文本值控件。
DeviceListAdapter:设备卡片列表与状态机
DeviceListAdapter 是数据层最复杂的适配器,负责设备连接页的设备卡片渲染、编辑模式与多设备形态适配:
public class DeviceListAdapter extends BaseQuickAdapter<HistoryDevice, BaseViewHolder> {
private final static String TAG = DeviceListAdapter.class.getSimpleName();
private final IDeviceConnectContract.IDeviceConnectPresenter mPresenter;
private final RCSPController mRCSPController = RCSPController.getInstance();
private volatile boolean isEditMode;
private OnDeleteItemListener mOnDeleteItemListener;
private OnLocationItemListener mOnLocationItemListener;
private final static int DEVICE_STATUS_DISCONNECTED = 0;
private final static int DEVICE_STATUS_CONNECTED = 1;
private final static int DEVICE_STATUS_USING = 2;
private final static int DEVICE_STATUS_CONNECTING = 3;
private final static int DEVICE_STATUS_MANDATORY_UPGRADE = 4;
public DeviceListAdapter(@NonNull IDeviceConnectContract.IDeviceConnectPresenter presenter) {
super(R.layout.item_device_list);
mPresenter = presenter;
}
public void setOnDeleteItemListener(OnDeleteItemListener onDeleteItemListener) {
mOnDeleteItemListener = onDeleteItemListener;
}
public void setOnLocationItemListener(OnLocationItemListener onLocationItemListener) {
mOnLocationItemListener = onLocationItemListener;
}
public boolean isEditMode() {
return isEditMode;
}
@SuppressLint("NotifyDataSetChanged")
public void setEditMode(boolean editMode) {
if (editMode && !checkDataArrayHasDisconnectDevice()) return;
if (isEditMode != editMode) {
if (editMode) {
if (getData().size() == 1 && getItem(0) != null &&
mPresenter.isConnectedDevice(getItem(0).getDevice().getAddress())) {
//因为条件不成立,不能进入编辑模式。
return;
}
}
isEditMode = editMode;
notifyDataSetChanged();
}
}
关键设计:
- 依赖注入:构造时要求传入
IDeviceConnectContract.IDeviceConnectPresenter,适配器通过 Presenter 查询连接状态(如isConnectedDevice(address)),保持对 UI 与蓝牙层的间接依赖。 - 单例 RCSP:
RCSPController.getInstance()是杰理 SDK 的全局控制器,适配器用它判断设备连接状态、触发连接等。 - 编辑模式守卫:
setEditMode(true)有两个前置校验——①列表中没有已断开设备则拒绝进入;②列表中只有一个设备且它已连接则拒绝进入(防止把当前正在使用的设备移入可删除/可排序状态)。校验失败时静默返回,不抛异常。 - 并发标记:
isEditMode声明为volatile,因编辑模式可能由列表项长按回调与 UI 线程同时读写,volatile保证可见性。 - 状态常量:5 个设备状态(断开/已连接/使用中/连接中/强制升级)映射到卡片 UI 的不同表现;适配器根据
RCSPController、DeviceStatusManager与ProductModel常量(充电仓待机/工作、左右耳连接/待机、产品 Logo)选择产品图片与状态文案,实现不同形态设备的自适应渲染。
stateDiagram-v2
[*] --> DISCONNECTED: 设备记录加载
DISCONNECTED --> CONNECTING: 用户点击连接
CONNECTING --> CONNECTED: 连接成功
CONNECTING --> DISCONNECTED: 连接失败/超时
CONNECTED --> USING: 被占用/正在使用
CONNECTED --> MANDATORY_UPGRADE: 固件强制升级
USING --> DISCONNECTED: 断开
MANDATORY_UPGRADE --> CONNECTED: 升级完成
状态机说明:设备卡片的状态迁移由蓝牙 SDK 回调驱动(连接成功/失败、占用变化、升级事件),适配器在 convert() 时读取当前状态渲染对应 UI;编辑模式(isEditMode)是该状态机的“平行维度”,仅在存在可编辑(已断开)设备时才可进入。
BindingUtil:DataBinding 数据注入
BindingUtil 位于 util 包,但承担数据层的绑定职责——通过 @BindingAdapter 扩展 XML 绑定表达式,其中 setRecycleViewData 直接向 BaseQuickAdapter 注入数据:
@BindingAdapter("android:selected")
public static void setViewSelected(View view, boolean selectedState) { ... }
@BindingAdapter("android:src")
public static void setSrc(ImageView view, int resId) { ... }
@BindingAdapter("android:loadSrc")
public static void setSrc(ImageView view, String url) { ... }
@BindingAdapter("android:loadGifSrc")
public static void setLoadGifSrc(ImageView view, int resourceId) { ... }
@BindingAdapter("android:loadFilletSrc")
public static void setFilletSrc(ImageView view, String url) { ... }
@BindingAdapter("android:setRecycleViewData")
public static void setRecycleViewData(View view, List list) {
RecyclerView recyclerView = (RecyclerView) view;
BaseQuickAdapter adapter = (BaseQuickAdapter) recyclerView.getAdapter();
adapter.setNewInstance(list);
}
设计要点:
- 重写系统
android:src以支持 Glide 异步加载(URL/资源 ID/圆角/GIF 变体),把图片加载策略从 XML/代码中抽离。 android:setRecycleViewData是 MVVM 风格的列表绑定:XML 中app:setRecycleViewData="@{viewModel.deviceList}"即可全量刷新 RecyclerView,前提是getAdapter()返回的是BaseQuickAdapter——这正是数据层统一基座带来的红利。
Core Flow:设备列表渲染与编辑流程
sequenceDiagram
participant A as 设备连接页 (ui.device)
participant P as IDeviceConnectPresenter
participant DA as DeviceListAdapter
participant R as RCSPController
participant D as DeviceStatusManager
A->>DA: new DeviceListAdapter(presenter)
A->>DA: setOnDeleteItemListener / setOnLocationItemListener
A->>P: 加载历史设备列表
P-->>A: List<HistoryDevice>
A->>DA: setNewInstance(historyDeviceList)
loop 每个设备条目
DA->>DA: convert(helper, item)
DA->>R: getInstance() 查询连接状态
DA->>D: 查询设备使用状态
D-->>DA: 状态 (连接中/已连接/使用中/强制升级)
DA->>DA: 按 ProductModel 选择产品图/状态文案
end
A->>DA: setEditMode(true)
DA->>DA: checkDataArrayHasDisconnectDevice() 校验
alt 存在已断开设备 且 非单机已连接
DA->>DA: isEditMode = true
DA->>DA: notifyDataSetChanged() 显示删除/排序控件
else 校验不通过
DA-->>A: 静默返回 (不进入编辑模式)
end
流程要点:
- 页面构造适配器时注入 Presenter(
IDeviceConnectContract.IDeviceConnectPresenter),并注册删除/排序回调监听器——事件“上抛”到页面层处理,适配器不持有业务逻辑。 - 数据加载走 Presenter(MVP 惯例),页面拿到
List<HistoryDevice>后通过setNewInstance一次性注入。 - 每个条目渲染时,
convert()实时向RCSPController/DeviceStatusManager查询设备状态,配合ProductModel常量选择产品图片,因此设备状态变化后只需再次notifyDataSetChanged即可整体刷新。 - 编辑模式是“有条件的”UI 状态:只有存在已断开设备、且不是“唯一已连接设备”时才允许进入,避免用户误删正在使用的设备。
Usage Examples
示例一:创建并填充功能设置列表
// 功能设置页创建 FunctionAdapter 并注入设置项
FunctionAdapter adapter = new FunctionAdapter();
adapter.setNewInstance(settingsItems); // List<SettingsItem>
recyclerView.setAdapter(adapter);
示例二:设备列表编辑模式的进入条件
@SuppressLint("NotifyDataSetChanged")
public void setEditMode(boolean editMode) {
if (editMode && !checkDataArrayHasDisconnectDevice()) return;
if (isEditMode != editMode) {
if (editMode) {
if (getData().size() == 1 && getItem(0) != null &&
mPresenter.isConnectedDevice(getItem(0).getDevice().getAddress())) {
//因为条件不成立,不能进入编辑模式。
return;
}
}
isEditMode = editMode;
notifyDataSetChanged();
}
}
示例三:通用设置项的值类型分支
switch (item.getType()) {
case ValueType.IMAGE:
helper.setVisible(R.id.item_value, false);
default:
break;
case ValueType.TEXT:
TextView tvText = helper.getView(R.id.item_value);
tvText.setText(item.getValue());
helper.setVisible(R.id.item_next_logo, false);
break;
}
示例四:DataBinding 注入列表数据
@BindingAdapter("android:setRecycleViewData")
public static void setRecycleViewData(View view, List list) {
RecyclerView recyclerView = (RecyclerView) view;
BaseQuickAdapter adapter = (BaseQuickAdapter) recyclerView.getAdapter();
adapter.setNewInstance(list);
}
以上示例展示了数据层的三种典型用法:纯代码装配(示例一)、带业务守卫的列表状态控制(示例二)、类型化列表项渲染(示例三)以及 XML 声明式数据注入(示例四)。新增设备功能时,通常只需:新建/复用 SettingsItem 模型 → 选择或新建适配器 → 在页面中 setNewInstance 注入。
API Reference
FunctionAdapter extends BaseQuickAdapter<SettingsItem, BaseViewHolder>
| 成员 | 签名 | 说明 |
|---|---|---|
| 构造器 | FunctionAdapter() | 使用 R.layout.item_key_settings_two 布局,无初始数据 |
| 重写 | void convert(BaseViewHolder helper, SettingsItem item) | 绑定图标、名称、值;按 isShowIcon() 控制图标显隐 |
CommonListAdapter extends BaseQuickAdapter<SettingsItem, BaseViewHolder>
| 成员 | 签名 | 说明 |
|---|---|---|
| 常量 | ValueType.IMAGE = 0 / ValueType.TEXT = 1 | 列表项值类型 |
| 构造器 | CommonListAdapter(List<SettingsItem> list) | 使用 R.layout.item_common_list 布局并携带初始数据 |
| 重写 | void convert(BaseViewHolder helper, SettingsItem item) | 绑定标题;末项隐藏分割线;按类型渲染图片/文本值 |
DeviceListAdapter extends BaseQuickAdapter<HistoryDevice, BaseViewHolder>
| 成员 | 签名 | 说明 |
|---|---|---|
| 构造器 | DeviceListAdapter(IDeviceConnectContract.IDeviceConnectPresenter presenter) | 必须注入 Presenter,用于查询连接状态 |
| 设置 | void setOnDeleteItemListener(OnDeleteItemListener l) | 注册删除回调(编辑模式下点击删除) |
| 设置 | void setOnLocationItemListener(OnLocationItemListener l) | 注册排序/位置调整回调 |
| 查询 | boolean isEditMode() | 当前是否处于编辑模式 |
| 操作 | void setEditMode(boolean editMode) | 切换编辑模式;带前置守卫校验(存在已断开设备、非唯一已连接设备),校验失败静默返回 |
| 常量 | DEVICE_STATUS_DISCONNECTED=0 / CONNECTED=1 / USING=2 / CONNECTING=3 / MANDATORY_UPGRADE=4 | 设备卡片状态 |
各适配器还继承了
BaseQuickAdapter的公共 API:setNewInstance(List<T>)、getData()、getItem(int)、getItemCount()、getItemPosition(T)、notifyDataSetChanged()、addData(T)、remove(T)等。
Failure Modes、边界与并发
编辑模式守卫(防误操作)
setEditMode(true) 的两道守卫(无已断开设备、单机且已连接)失败时静默返回而非抛异常,调用方(长按回调)不会崩溃,但 UI 表现为“无法进入编辑模式”。若列表为空,checkDataArrayHasDisconnectDevice() 返回 false,同样拒绝进入。
空数据与空条目
FunctionAdapter.convert()显式判空item != null,防止 RecyclerView 复用脏数据导致 NPE。CommonListAdapter中getItemCount() > 0 && position == getItemCount() - 1同时保护空列表与末项分割线逻辑。
并发与可见性
isEditMode声明为volatile:编辑模式的读写可能来自 UI 线程(列表项长按)与主线程状态回调,volatile保证跨线程可见性但不保证原子性;由于切换逻辑集中在setEditMode且无复合读改写,当前设计下是安全的。RCSPController为进程级单例(getInstance()),多个适配器/页面共享同一实例,其内部线程模型由 SDK 保证;适配器仅在主线程读取状态。
类型不匹配风险
BindingUtil.setRecycleViewData 将 getAdapter() 强制转换为 BaseQuickAdapter。若 RecyclerView 绑定了非 BRVAH 适配器,会在 XML 数据绑定触发时抛出 ClassCastException——这是数据层统一基座的隐含约束:所有 RecyclerView 适配器必须是 BaseQuickAdapter 子类,新增页面时须遵守。
Performance 与运行注意事项
- 全量刷新为主:
setEditMode使用notifyDataSetChanged()(并标注@SuppressLint("NotifyDataSetChanged")),编辑模式切换时整体重绘卡片;列表规模为历史设备(通常个位数),成本可接受。大列表场景应改用 BRVAH 的差量更新 API。 - 图片加载:
DeviceListAdapter通过 Glide 加载产品图,并设置DiskCacheStrategy与SIZE_ORIGINAL(见其导入与静态常量),减少重复网络/磁盘读取。 - 数据注入时机:
setNewInstance是 O(n) 全量替换;数据绑定场景下每次列表变化都会触发,应避免高频调用(如每帧刷新)。 - 日志:适配器持有
TAG = DeviceListAdapter.class.getSimpleName(),异常路径可据此在 logcat 中过滤定位。
Extension Points
- 新增功能适配器:继承
BaseQuickAdapter<T, BaseViewHolder>,构造传入新布局,重写convert()绑定模型字段。若列表项语义仍是“名称/值/图标”,直接复用FunctionAdapter或CommonListAdapter+SettingsItem即可,无需新类。 - 设备形态适配:
DeviceListAdapter依据ProductModel常量(充电仓待机/工作、左右耳连接/待机、产品 Logo)选择产品图与文案;新增设备形态时扩展该映射逻辑(配合ProductUtil/DefaultResFactory),适配器结构无需变更。 - 交互回调:删除与排序通过
OnDeleteItemListener、OnLocationItemListener上抛,页面实现即可扩展行为(如二次确认弹窗、位置持久化),适配器保持可复用。 - 值类型扩展:
CommonListAdapter.ValueType目前仅IMAGE/TEXT;新增类型(如开关、进度条)时在convert()的switch中追加分支即可,注意保持 switch 的 fall-through 语义清晰。 - DataBinding 自定义绑定:
BindingUtil的@BindingAdapter是集中扩展点,可新增android:xxx绑定属性(如圆角图、GIF、列表差量更新)而无需改动各页面代码。
Tests
本目录源码中未发现针对 data.adapter 包的单元测试文件(仓库测试主要覆盖蓝牙 SDK 与工具类)。适配器逻辑以手工/仪器化验证为主:编辑模式守卫、状态常量映射、分割线末项隐藏等行为通过设备连接页交互验证。新增适配器时建议至少覆盖:空数据渲染、setEditMode 边界、ValueType 分支。
Related Links
- SettingsItem 数据模型 —— 功能/设置列表项模型
- HistoryDevice 数据模型 —— 历史设备模型(包装蓝牙 SDK 的
HistoryBluetoothDevice) - IDeviceConnectContract —— 设备连接页 MVP 契约(Presenter 注入适配器)
- RCSPController —— 杰理蓝牙 SDK 全局控制器(设备连接状态来源)
- BindingUtil —— DataBinding 自定义绑定适配
- 相关兄弟页面:蓝牙连接与 RCSP 命令、UI 页面层(
ui.*)、设备工具(tool.*)