fix(android): 回填卡片识别不适配 WebView 订单列表,cards() 命中 0 #309

Open
opened 2026-09-18 11:25:36 +08:00 by ila · 0 comments
Owner

来源:2026-09-18 真机现场分析。依赖 #308。

问题

回填扫描停在 PDD「我的订单」列表,cards() 命中 0 个。真机现场(427 节点)实测两个独立原因:

一、订单卡片在无障碍树里不可点。

全页仅 36 个 clickable=true 节点:顶栏 2 个 + 每张卡片的动作按钮(确认收货/查看物流/申请退款/更多/催发货/再次拼单/删除订单)。没有代表整张卡片的可点容器。 而 cards() 第一关就是 safe(),safe() 要求 node.clickable,因此永远返回空。

二、图片类名判据与页面不符。

children.any { it.className?.endsWith("ImageView") == true }

该页为 WebView 渲染,商品图类名是 android.widget.Image(56 个),整页只有 1 个真正的 ImageView。即使卡片容器可点,该条也过不了。

实测:通过 safe() 的 16 个可点节点逐条评估 cards(),命中 0。

现场结构(第一张卡片)

卡片容器  [0,369][1080,1122]  高 753   clickable=false
  店铺名   远信运动专营店            [114,396][399,447]
  商品图   class=Image              [33,471][273,711]
  商品标题 拐杖老人手杖带灯防滑…      [291,471][876,585]  TextView
  动作行   查看物流/确认收货/申请退款  [180,900][1047,990]  ← 仅此排 clickable

父容器 .../3 高 1959 是整个列表,卡片是 .../3/0 高 753,用「最小满足条件的容器」可区分。

方案

  1. 卡片识别改为结构识别,不要求 clickable:子树同时包含图片节点、≥4 字文本、动作行标签,取最小满足容器。
  2. 图片类名判据接受 Image 与 ImageView 两种。

[必须] 本工单只修正识别,不改变点击方式。识别出的卡片此时仍无法点击(驱动要求 isClickable),扫描仍会失败,但失败原因会从「卡片无法安全识别」变为点击失败——这是预期的中间状态,点击能力由 后续工单 决定。

非目标

  • 不放宽 safe() 的禁词与几何重叠规则。
  • 不引入坐标盲点或祖先点击。
  • 不改 list() / allTab() / expansion()。

验收

  • 用真机现场树作为夹具,cards() 能识别出卡片容器且数量与页面可见订单数一致。
  • 列表容器本身不被误识别为卡片。
  • 动作按钮不被识别为卡片。
  • 既有单测全部通过。

验证

./gradlew :app:testDebugUnitTest,新增基于真机树的夹具用例。

文档影响

卡片识别判据变化,按实际改动同步 Business-Rules-and-Glossary。

> 来源:2026-09-18 真机现场分析。依赖 #308。 ## 问题 回填扫描停在 PDD「我的订单」列表,`cards()` 命中 0 个。真机现场(427 节点)实测两个独立原因: **一、订单卡片在无障碍树里不可点。** 全页仅 36 个 `clickable=true` 节点:顶栏 2 个 + 每张卡片的动作按钮(确认收货/查看物流/申请退款/更多/催发货/再次拼单/删除订单)。**没有代表整张卡片的可点容器。** 而 `cards()` 第一关就是 `safe()`,`safe()` 要求 `node.clickable`,因此永远返回空。 **二、图片类名判据与页面不符。** ```kotlin children.any { it.className?.endsWith("ImageView") == true } ``` 该页为 WebView 渲染,商品图类名是 `android.widget.Image`(56 个),整页只有 1 个真正的 `ImageView`。即使卡片容器可点,该条也过不了。 实测:通过 `safe()` 的 16 个可点节点逐条评估 `cards()`,命中 0。 ## 现场结构(第一张卡片) ``` 卡片容器 [0,369][1080,1122] 高 753 clickable=false 店铺名 远信运动专营店 [114,396][399,447] 商品图 class=Image [33,471][273,711] 商品标题 拐杖老人手杖带灯防滑… [291,471][876,585] TextView 动作行 查看物流/确认收货/申请退款 [180,900][1047,990] ← 仅此排 clickable ``` 父容器 `.../3` 高 1959 是整个列表,卡片是 `.../3/0` 高 753,用「最小满足条件的容器」可区分。 ## 方案 1. 卡片识别改为**结构识别,不要求 `clickable`**:子树同时包含图片节点、≥4 字文本、动作行标签,取最小满足容器。 2. 图片类名判据接受 `Image` 与 `ImageView` 两种。 `[必须]` 本工单只修正识别,**不改变点击方式**。识别出的卡片此时仍无法点击(驱动要求 `isClickable`),扫描仍会失败,但失败原因会从「卡片无法安全识别」变为点击失败——这是预期的中间状态,点击能力由 后续工单 决定。 ## 非目标 - 不放宽 `safe()` 的禁词与几何重叠规则。 - 不引入坐标盲点或祖先点击。 - 不改 `list()` / `allTab()` / `expansion()`。 ## 验收 - [ ] 用真机现场树作为夹具,`cards()` 能识别出卡片容器且数量与页面可见订单数一致。 - [ ] 列表容器本身不被误识别为卡片。 - [ ] 动作按钮不被识别为卡片。 - [ ] 既有单测全部通过。 ## 验证 `./gradlew :app:testDebugUnitTest`,新增基于真机树的夹具用例。 ## 文档影响 卡片识别判据变化,按实际改动同步 Business-Rules-and-Glossary。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#309