--- name: project-binding-adapter-from-source description: >- 根据用户给出的 Java/Kotlin 工具类或 View 相关源码,设计并实现 androidx.databinding.BindingAdapter。 工作区内优先 Read lib/DevSimple 下 bindingadapters;范例文件表见 references/reference.md。 过滤不适合 XML 的 API;Long? 时间戳、Boolean? 三态、attribute 合并(参照 XYI);成稿按 code-method-normalize。 在用户要求生成 BindingAdapter、补全 DataBinding 属性、或评审 DevSimple bindingadapters 时使用。 --- # 从源码生成 DataBinding BindingAdapter ## 模块路径(本地优先,与 viewtheme 共用锚点) | 符号 | 当前值(DevUtils) | |------|-------------------| | `DEVSIMPLE_ROOT` | `lib/DevSimple` | | `BINDING_VIEW_DIR` | `{DEVSIMPLE_ROOT}/src/main/java/dev/simple/bindingadapters/view` | | `BINDING_ATTR_DIR` | `{DEVSIMPLE_ROOT}/src/main/java/dev/simple/bindingadapters/view/attribute` | 存在 `{DEVSIMPLE_ROOT}` 时 **Read 本地 `*.kt`**,勿用 WebFetch 代替。生成前在 `{BINDING_VIEW_DIR}` 下列出并对照现有实现;**优先范例文件名、前缀惯例、判定表** → [references/reference.md](references/reference.md)。 **上游 fallback**(仅工作区无模块时):https://github.com/afkT/DevUtils/tree/master/lib/DevSimple ## 必读搭配 生成或改写 **方法体与文档** 时,**同时 Read 并遵守**: - [code-method-normalize/SKILL.md](../code-method-normalize/SKILL.md)(JavaDoc/KDoc、`
`、首段规则、`@param`/`@return`、`boolean` 对举、异常内消化等)

本 Skill 负责 **「是否该做 BindingAdapter、属性如何设计、时间戳与合并参数」**;文档句式交给 **code-method-normalize**。

## 风格对齐(执行要点)

1. **Read** `{BINDING_VIEW_DIR}` 下与目标控件相关的 `*.kt`(至少一份同族文件 + [references/reference.md §优先范例](references/reference.md#优先范例文件按主题选读) 中对应行)。
2. 合并参数实体放在 `{BINDING_ATTR_DIR}`;`XYI` 等写法对照同目录 `XYI.kt`。
3. 文件头「不适合 BindingAdapter」的说明以 **`View.kt`** 为准(路径见 reference)。

---

## 1. 核心门槛:方法是否适合 BindingAdapter + XML

BindingAdapter 绑定在 **布局里的单个 View 节点**;只封装 **对该接收者 View(及合理子类型)有意义、且能在 XML 表达参数** 的操作。

**应跳过或不要求用户封装**(与 `View.kt` 文件头说明一致)的典型类别:

- 需要 **LayoutInflater / 非当前节点上下文** 的:`inflate` 等
- 返回 **Activity / Fragment / 任意非 View** 且无法在绑定中作为稳定入参的:`getActivity` 等
- 依赖 **任意子下标访问**、且语义不属于「当前节点自身配置」的:`getChildAt` 等(除非业务明确绑定的是容器且参数是 index,再按列表类控件单独设计)
- **ViewGroup 结构转换** 等与当前节点单次属性语义不符的:`convertViewGroup` 等

**适合封装的正向例子**:`removeAllViews`、`requestLayout`、`setText`、滚动到某位置、设置可见性、设置图片资源等——参数为基本类型、`String`、Drawable、`XYI` 等可在 Binding / LiveData 中传递的类型。

若某方法 **仅 `View`/`TextView`/… 一个参数**,除「纯状态同步」(如设置某个由 XML 已绑定的 bool)外,**重复触发**(同值不刷新)往往成问题 → 见第 2 节 **时间戳**。

---

## 2. 仅 View(接收者)型副作用:`Long?` 时间戳触发

当底层 API 形如 `FooUtils.action(view)` **仅有当前接收者、无业务参数**,又需要在 MVVM 里 **多次触发同一逻辑**(例如再次滑到底、再次清空)时:

- **唯一绑定入参** 使用 `timestamp: Long?`(或与其它参数组合时,时间戳作为触发列之一)
- 在适配器开头使用 **与项目一致的正时间戳判断**(见第 3 节)
- 在 KDoc `
` 中写明:建议绑定 `System.currentTimeMillis()` 或 ViewModel 内递增时间戳;**优于** `LiveData` 固定 `true` 不二次刷新

**成对语义(示例)**:已有 `EditTextUtils.setText(view, text)` 对应「设文本」;可另增 **「清空文本」** 适配器,入参仅为 `timestamp: Long?`,内部在通过校验后调用 `EditTextUtils.setText(view, "")`(或项目等价 API)。文档中说明与 `binding_et_text` 等的关系,避免重复绑定冲突。

---

## 2.5 Boolean 三态开关(开启 / 关闭 / 不修改)

当底层工具类提供 **成对的 set / remove**(或语义等价的 enable / disable)API,且布局需要绑定 **可切换、可保持的 View 效果**(下划线、删除线、`paintFlags` 装饰、部分可见性样式等)时:

- 绑定入参使用 **`Boolean?`**(不用非空 `Boolean`,以便区分「未绑定」与「显式关闭」)。
- 适配器内统一约定:
  - **`null`** → `return`,**不修改** View(未提供 LiveData、或尚未赋值时保留 XML / 主题默认)。
  - **`true`** → 调用 **开启 / 设置** API(如 `TextViewUtils.setUnderlineText`)。
  - **`false`** → 调用 **关闭 / 移除** API(如 `TextViewUtils.removeUnderlineText`)。

与 **§2 时间戳** 的分工:

| 场景 | 选用 |
|------|------|
| 需要 **同步并保持** 开/关状态,随 DataBinding 刷新更新 UI | **`Boolean?` 三态**(本节) |
| **无状态**,每次都要 **再触发一次** 副作用(再次滚到底、再次清空) | **`Long?` 时间戳**(§2) |
| 工具类 **仅有 set、无 remove**,且 `false` 语义就是「不做事」 | 可 `Boolean?` 仅 `true` 时执行;若业务要在布局里 **关掉** 已有效果,应补 remove 工具方法后改本节三态 |

**禁止**:底层已有 `removeXxx` 时仍写 `if (flag != true) return`,导致布局绑 `false` 无法撤销效果。

**仓库参考实现**:`TextView.bindingTVUnderline`、`TextView.bindingTVStrikeThru`(见 `{BINDING_VIEW_DIR}/TextView.kt`,reference §优先范例)。

```kotlin
fun TextView.bindingTVUnderline(
    underline: Boolean?,
    antiAlias: Boolean?,
) {
    if (underline == null) return
    if (underline) {
        TextViewUtils.setUnderlineText(this, antiAlias ?: true)
    } else {
        TextViewUtils.removeUnderlineText(this)
    }
}
```

- 可选第二参数(如 `antiAlias`)仅在 **`true` 开启分支** 使用;`requireAll = false` 时缺省与工具类一致。
- KDoc `
` 中写明三态与对应的 `set*` / `remove*` 工具方法名。

---

## 3. 时间戳判定:可复用扩展

逻辑统一为:**非 null 且大于 0** 才执行。

本仓库已有:

```kotlin
fun Long?.qualifiesScroll(): Boolean = this != null && this > 0L
```

**新代码**若语义不是滚动(避免命名误导),可在同一模块抽取例如:

```kotlin
/** 数据绑定用:时间戳非空且大于 0 时执行一次副作用。 */
fun Long?.qualifiesBindingAction(): Boolean = this != null && this > 0L
```

具体命名与存放文件随模块惯例;**禁止**用 `<=0` 或 `null` 仍执行(除非项目已有特例并在备注中写明)。

---

## 4. 多参数合并为实体类(减少绑定次数与属性数量)

**避免** 多个密切相关、应同事务触发的量拆成多个 `binding_*`,导致 **多次调用适配器**、LiveData 数量膨胀。

做法:

1. 在 `dev.simple.bindingadapters.view.attribute`(即 `{BINDING_ATTR_DIR}`,或用户指定包的 `attribute` 子包)新增 **Kotlin data class / open class**,字段覆盖原工具方法参数(含可选默认值)。
2. **单个** `@BindingAdapter("binding_…_single_snake_name")`,入参为该类型可空;`null` 时跳过。
3. 命名与 **XYI** 对齐习惯:简短后缀表意(如位移用 `XYI`);补充 `equals`/`hashCode`(data class 自带)、`toString`、伴生工厂方法视需要。
4. 迁移示例:由 `binding_scroll_instant_rel_dx` + `binding_scroll_instant_rel_dy`(`requireAll = false`)改为 **`binding_scroll_instant_rel_xy`** + `XYI?`(见用户提供的旧/新对照)。

---

## 5. 布局属性与 Kotlin 命名

- XML:`app:binding_snake_case`;`@BindingAdapter` 的 `value` 与之一致。
- Kotlin 扩展:`fun View.bindingCamelCase(...)`,与文件内现有风格一致。
- `requireAll = false` 仅当 **确有独立缺省语义** 时使用;能合并为实体类则优先合并。
- 有返回值时:优先 `Boolean` 表示是否执行/成功,并满足 **code-method-normalize** 中 `boolean`/`Boolean` 的 `@return` 写法。

---

## 6. 生成工作流(Agent 执行顺序)

1. **Read** 用户给出的源文件,列出候选 `public`/`internal` 方法。
2. **过滤** 第 1 节不适配 XML 的 API。
3. 对每个保留方法:**映射接收者类型**(`View` / `TextView` / `ImageView` / `ViewGroup` / `RecyclerView` 等)、**参数拆分或合并**(第 4 节)、**是否需要时间戳**(第 2 节)或 **Boolean 三态开关**(第 2.5 节)。
4. 落文件:新适配器写入 `{BINDING_VIEW_DIR}/`(按控件分文件,与现有 `TextView.kt`、`ViewScroll.kt` 等并列);attribute 类写入 `{BINDING_ATTR_DIR}/`。
5. **Read** `code-method-normalize` 并整理文档与返回类型。
6. 自检:`@BindingAdapter` 不与 `{BINDING_VIEW_DIR}` 内已有属性名冲突;`try/catch` 与日志风格参照 reference 中 **TextView** / **ImageViewLoadNative** 范例。

判定表、XML 示例、`binding_*` 前缀表见 [references/reference.md](references/reference.md)。

## 执行清单

- [ ] 已 Read 本地 `{BINDING_VIEW_DIR}` 或 reference §优先范例,而非仅凭记忆写适配器。
- [ ] 已对照 reference §DevSimple 前缀惯例,避免 `binding_*` 冲突。
- [ ] 新 attribute 类落在 `{BINDING_ATTR_DIR}`;成稿前已 Read `code-method-normalize`。