「观影4x4和4x6,只有六张图,划到第七张,就没图片了」
设备 5NC0225B27004718,当前版本 v95(b84ea06)。 实测确认:观影 4x4 / 6x4 卡的海报墙是两列可滚动列表,前 6 格有海报,第 7 格起全是淡紫占位块(只有片名文字)。
5NC0225B27004718
b84ea06
entry/src/main/ets/data/FbCardMediaData.ets:
entry/src/main/ets/data/FbCardMediaData.ets
const CARD_IMG_MAX: number = 6;
rows.length < 30
keys.length < CARD_IMG_MAX
map
img
img === ''
卡片侧 FbMovieCard4x4.ets:82-104:img === '' 时走 else 分支画淡紫底 + 片名。 ⇒ 数据侧只配 6 张图,却让卡片渲染 30 格,多出来的格子必然空。 渲染逻辑本身没错,是「配图数量」和「列表长度」两个上限不一致。
FbMovieCard4x4.ets:82-104
else
华为 Form Kit FAQ(faqs-technology-54)原文:
faqs-technology-54
从 API version 20 开始,图片文件数量上限为 20 张,所有图片总内存大小不超过 10MB, 超出限制的图片会显示异常,API version 19 及之前的版本,图片文件数量上限为 5 张,每张限制内存 2MB。
本机 const.ohos.apiversion = 26 ⇒ 走 20 张 / 10MB 这一档。 现有缩图 THUMB_W=240 / THUMB_H=360,解码后每张 240×360×4 = 0.33 MB:
const.ohos.apiversion = 26
THUMB_W=240 / THUMB_H=360
240×360×4 = 0.33 MB
让「配图数量」和「列表长度」用同一个常量,不再各写一个数:
CARD_IMG_MAX
6
30
FbCardImages.ets
MAX_FILES = 24
map.size
Image
Column
THUMB_W×THUMB_H×4×N
提交:f0d0ab9(fix(card): 观影 4x4/6x4 海报墙「只有 6 张图」修复 #2)
f0d0ab9
fix(card): 观影 4x4/6x4 海报墙「只有 6 张图」修复 #2
data/FbCardMediaData.ets
CARD_ROW_MAX = 30
list
img !== ''
fbAttachPosters
(recent, list)
(rows)
data/FbCardImages.ets
fbCardImagePaths
MAX_PARALLEL = 4
MAX_FILES
单纯把 6 调大,下次谁再把列表上限改回去,同样的 bug 会原地复活。 所以真正的修法是让格子数由图数量决定:list 只收配到图的行, 长度天然 ≤ CARD_IMG_MAX。这样哪怕某张海报 404、或某条本来就没 poster_key, 也只是少一格,不会冒出一个淡紫占位块。
poster_key
同时把两个数字都命名成常量(原来是裸字面量 30), 任何一处改动都必须显式写出来,不再是"两个各写各的"。
20 张图如果还用 for + await 串行,就是 20 次网络往返。 单张 connectTimeout 6000 + readTimeout 10000,实测会拖到 10~17 秒, 而卡片刷新的窗口根本撑不住这么久 —— 表现会是"刷新一半,图只出来几张"。 ⇒ 走固定并发窗口,MAX_PARALLEL = 4:20 张压到 5 轮,又不会一次开 20 个连接。
for + await
connectTimeout 6000 + readTimeout 10000
240 × 360 × 4 = 0.33MB 每张 × 20 张 = 6.59MB < 10MB,留 3.4MB 余量。
240 × 360 × 4 = 0.33MB
⚠️ 别为了"更清晰"去调大 THUMB_W/H:涨到 360×540 就是 0.74MB/张, 20 张 14.8MB 直接超限,表现是「部分图片显示异常 / 整卡白掉」,而且不报错。
THUMB_W/H
① 静态守卫:1317 通过 / 0 失败(新增 groupV96() 15 条断言)
1317 通过 / 0 失败
groupV96()
② 反向验证:15 通过 / 0 失败 —— 7 条注入,每条都恰好命中自己的目标断言:
15 通过 / 0 失败
THUMB
③ 编译:BUILD SUCCESSFUL in 13s,产物 entry-default-signed.hap 6.2M
BUILD SUCCESSFUL in 13s
entry-default-signed.hap
第一版断言写的是 CARD_IMG_MAX >= 4。而 bug 的原始值正好就是 6, 6 >= 4 恒真 ⇒ 把 6 注回去,守卫照样全绿,这条断言什么都没守。
CARD_IMG_MAX >= 4
6 >= 4
已在第二版改成 > 6,理由直接来自用户原话「划到第七张就没图片了」: 第 7 格必须有图,图数就不可能停在 6。
> 6
教训:判据必须穿过 bug 值本身。写「≥ 某个看着合理的下限」时, 先问一句「bug 值本身能通过吗」—— 能通过,这条断言就是假的。
hdc list targets
FbCard4x4
设备 5NC0225B27004718,装机版本 f0d0ab9(v96)。
这个 bug 编译不报错、运行也不报错,静态守卫只能守"代码里的数字关系", 守不住"第 7 格真的出图了"。所以必须滚一遍、数 Image 节点。
判据不是看颜色(我看不了图),而是靠真机节点树里两种形态的结构差异:
有图 Stack > [Image, Column(黑条: 片名 + 日期)] 无图 Stack > [Column(淡紫底: 片名), Column(黑条: 片名 + 日期)]
⇒ 判据 = Stack 的直接子节点里有 type == 'Image'。
Stack
type == 'Image'
两张卡都是逐屏滚到底扫过来的(不是只看底部一屏 —— 只看底部可能只覆盖第 15~18 格, 而 bug 恰恰在第 7 格):
观影 4×4(967×1025 px)
逐屏扫过 18 格: 神探之痕迹 / 重生2 / 欢迎来龙餐馆 / 八仙! / 火遮眼 / 给阿嬷的情书 / 挽救计划 / 镖人:风起大漠 / 夜王 / 熊出没·年年有熊 / 飞驰人生3 / 东北警察故事3 / 寻秦记 / 用武之地 / 疯狂动物城2 / 青蛇 / 捕风追影 / 浪浪山小妖怪 ✅ 18 格 > 6(6 是 bug 原值,必须超过它) ✅ 全部 18 格都有 Image(无图格:无) ✅ 同一格跨屏观测一致 ✅ 确实滚动过
观影 6×4(967×1600 px):同样 18 格,同样全部有图。
6 就是 CARD_IMG_MAX 的旧值、也就是这个 bug 的本体。闸值定成「≥ 4」这种 "看着合理的下限"时,6 >= 4 恒真 ⇒ 把 6 注回去守卫照样全绿,等于没守。 所以闸值必须穿过 bug 值本身,理由直接来自用户原话: 「划到第七张就没图片了」⇒ 第 7 格必须有图 ⇒ 有图的格数不可能停在 6。
v96 动的是 FbCardMediaData.ets 里两张卡共用的数据装配,所以一并验了:
FbCardMediaData.ets
神探之痕迹/重生2/欢迎来龙餐馆
7月22日/火遮眼/给阿嬷的情书
今天 10月28日 周三
十九
卡片高度阈值把 4×4 漏了:本机三种观影卡高度是 2×4 = 450px、4×4 = 1025px、6×4 = 1600px。阈值最初写 > 1200, 只命中 6×4,4×4 被静默跳过 —— 而用户原话是「4x4 和 4x6」两张。 现改为 > 900 + 按高度分类,两张分别验。
2×4 = 450px
4×4 = 1025px
6×4 = 1600px
> 1200
> 900
「?? 格」其实是滚动中间态,不是无图:滚动动画中途, ListItem 会先建出 Stack > Image,底部信息条 Column 还没建出来, 于是 texts() 为空、高度只有 496(正常 636)。 这类格子混进统计会变成假标题 '?'(多个不同格子撞同一个 key), 偶尔连 Image 都还没建 ⇒ 误报"无图"。 现改为「有底部信息条且有文本」才算渲染完成才参与判定; 真 bug 的格子是 Column(淡紫底+片名) + Column(黑条)、有文本,不会被漏掉。
ListItem
Stack > Image
texts()
'?'
Column(淡紫底+片名) + Column(黑条)
这两条与 pitfalls #462(判据闸值放过了 bug 值)、#463(副本层级不对致基线就红) 同族:都属"看着全绿、其实没守住"。已一并记入。
pitfalls #462
#463
#2
提交:f0d0ab9(已推送到 main,b84ea06..f0d0ab9)
main
b84ea06..f0d0ab9
entry/src/main/ets/data/FbCardMediaData.ets + FbCardImages.ets
apiversion = 26
240×360×4×20 = 6.59MB
核心思路不是"把 6 改成 20 就完事",而是让格子数由图数量决定: 这样哪怕某张海报 404、或某条本来就没 poster_key,也只是少一格, 不会冒出一个淡紫占位块。
entry/src/main
两条都已记入跨项目踩坑库(#462 / #463),避免同类问题再犯。
No dependencies set.
The note is not visible to the blocked user.
现象(用户原话)
设备
5NC0225B27004718,当前版本 v95(b84ea06)。实测确认:观影 4x4 / 6x4 卡的海报墙是两列可滚动列表,前 6 格有海报,第 7 格起全是淡紫占位块(只有片名文字)。
根因(已定位,不是渲染问题)
entry/src/main/ets/data/FbCardMediaData.ets:const CARD_IMG_MAX: number = 6;rows.length < 30keys.length < CARD_IMG_MAXmap里命中的行写imgimg === ''卡片侧
FbMovieCard4x4.ets:82-104:img === ''时走else分支画淡紫底 + 片名。⇒ 数据侧只配 6 张图,却让卡片渲染 30 格,多出来的格子必然空。
渲染逻辑本身没错,是「配图数量」和「列表长度」两个上限不一致。
官方规格(决定了最多能给多少张)
华为 Form Kit FAQ(
faqs-technology-54)原文:本机
const.ohos.apiversion = 26⇒ 走 20 张 / 10MB 这一档。现有缩图
THUMB_W=240 / THUMB_H=360,解码后每张240×360×4 = 0.33 MB:修复方案
让「配图数量」和「列表长度」用同一个常量,不再各写一个数:
CARD_IMG_MAX从6提到 20(官方上限,内存 6.59MB 安全)。30收敛到同一个常量 ⇒ 列表长度 = 图数量,结构上不可能再出现空网格。FbCardImages.ets的MAX_FILES = 24要 ≥ 新上限(20),目前够用,但写进注释绑定关系。列表按「实际拿到图的行数」收敛(
map.size),否则某个海报 404 时又会出现空格。验收标准
img非空(真机 dump 确认Image节点存在,不是占位Column)THUMB_W×THUMB_H×4×N断言)关联
进展:代码已改完、编译通过、静态验证全绿(真机待验)
提交:
f0d0ab9(fix(card): 观影 4x4/6x4 海报墙「只有 6 张图」修复 #2)改了什么(5 处,都在数据侧,卡片渲染一行没动)
data/FbCardMediaData.etsCARD_IMG_MAX6 → 20CARD_ROW_MAX = 30(命名常量),专给不画图的 2x2/2x4 纯文本行list:只保留img !== ''的行 ⇒ 格子数与图数结构上永远相等fbAttachPosters签名(recent, list)→(rows);任务卡同样的写死一并统一data/FbCardImages.etsfbCardImagePaths并发窗口(MAX_PARALLEL = 4);MAX_FILES24为什么不是「6 改成 20」就完事
单纯把 6 调大,下次谁再把列表上限改回去,同样的 bug 会原地复活。
所以真正的修法是让格子数由图数量决定:
list只收配到图的行,长度天然 ≤
CARD_IMG_MAX。这样哪怕某张海报 404、或某条本来就没poster_key,也只是少一格,不会冒出一个淡紫占位块。
同时把两个数字都命名成常量(原来是裸字面量
30),任何一处改动都必须显式写出来,不再是"两个各写各的"。
并发这件事为什么必须做
20 张图如果还用
for + await串行,就是 20 次网络往返。单张
connectTimeout 6000 + readTimeout 10000,实测会拖到 10~17 秒,而卡片刷新的窗口根本撑不住这么久 —— 表现会是"刷新一半,图只出来几张"。
⇒ 走固定并发窗口,
MAX_PARALLEL = 4:20 张压到 5 轮,又不会一次开 20 个连接。内存核算(按解码后算,不是按 jpg 体积)
240 × 360 × 4 = 0.33MB每张 × 20 张 = 6.59MB < 10MB,留 3.4MB 余量。⚠️ 别为了"更清晰"去调大
THUMB_W/H:涨到 360×540 就是 0.74MB/张,20 张 14.8MB 直接超限,表现是「部分图片显示异常 / 整卡白掉」,而且不报错。
验证结果
① 静态守卫:
1317 通过 / 0 失败(新增groupV96()15 条断言)② 反向验证:
15 通过 / 0 失败—— 7 条注入,每条都恰好命中自己的目标断言:CARD_IMG_MAX改回 6(bug 本体)30img !== ''过滤THUMB放大到 480×720(26MB)MAX_FILES降到 8(小于 20)for + await串行③ 编译:
BUILD SUCCESSFUL in 13s,产物entry-default-signed.hap6.2M🔴 反向验证抓出我自己的一个假绿断言(值得记一笔)
第一版断言写的是
CARD_IMG_MAX >= 4。而 bug 的原始值正好就是 6,6 >= 4恒真 ⇒ 把 6 注回去,守卫照样全绿,这条断言什么都没守。已在第二版改成
> 6,理由直接来自用户原话「划到第七张就没图片了」:第 7 格必须有图,图数就不可能停在 6。
待办
hdc list targets当前为空(只有 COM3/COM4 两个 UART 口),手机没连上。等接入后装机实测「滚到底最后一格有海报」。
FbCard4x4)。真机验收结果:已修复,4×4 与 6×4 两张都验过
设备
5NC0225B27004718,装机版本f0d0ab9(v96)。验证方法
这个 bug 编译不报错、运行也不报错,静态守卫只能守"代码里的数字关系",
守不住"第 7 格真的出图了"。所以必须滚一遍、数 Image 节点。
判据不是看颜色(我看不了图),而是靠真机节点树里两种形态的结构差异:
⇒ 判据 =
Stack的直接子节点里有type == 'Image'。结果
两张卡都是逐屏滚到底扫过来的(不是只看底部一屏 —— 只看底部可能只覆盖第 15~18 格,
而 bug 恰恰在第 7 格):
观影 4×4(967×1025 px)
观影 6×4(967×1600 px):同样 18 格,同样全部有图。
关键断言为什么是「> 6」
6就是CARD_IMG_MAX的旧值、也就是这个 bug 的本体。闸值定成「≥ 4」这种"看着合理的下限"时,
6 >= 4恒真 ⇒ 把 6 注回去守卫照样全绿,等于没守。所以闸值必须穿过 bug 值本身,理由直接来自用户原话:
「划到第七张就没图片了」⇒ 第 7 格必须有图 ⇒ 有图的格数不可能停在 6。
回归(改了共用代码,必须回头验)
v96 动的是
FbCardMediaData.ets里两张卡共用的数据装配,所以一并验了:神探之痕迹/重生2/欢迎来龙餐馆→7月22日/火遮眼/给阿嬷的情书),v95 的改动没被破坏今天 10月28日 周三/十九)顺带修掉的两个验收脚本自身的坑(不是产品问题,但会误导结论)
卡片高度阈值把 4×4 漏了:本机三种观影卡高度是
2×4 = 450px、4×4 = 1025px、6×4 = 1600px。阈值最初写> 1200,只命中 6×4,4×4 被静默跳过 —— 而用户原话是「4x4 和 4x6」两张。
现改为
> 900+ 按高度分类,两张分别验。「?? 格」其实是滚动中间态,不是无图:滚动动画中途,
ListItem会先建出Stack > Image,底部信息条Column还没建出来,于是
texts()为空、高度只有 496(正常 636)。这类格子混进统计会变成假标题
'?'(多个不同格子撞同一个 key),偶尔连 Image 都还没建 ⇒ 误报"无图"。
现改为「有底部信息条且有文本」才算渲染完成才参与判定;
真 bug 的格子是
Column(淡紫底+片名) + Column(黑条)、有文本,不会被漏掉。收尾
f0d0ab9(带#2)收尾:已修复并真机验收通过,关单
提交:
f0d0ab9(已推送到main,b84ea06..f0d0ab9)改了什么
entry/src/main/ets/data/FbCardMediaData.ets+FbCardImages.etsCARD_IMG_MAX6 → 20(华为 Form Kit FAQ:API 20+ 上限 20 张 / 10MB,本机
apiversion = 26走这一档;内存按解码后算240×360×4×20 = 6.59MB)CARD_ROW_MAX = 30(命名常量),专给不画图的 2×2/2×4 纯文本行list:只保留img !== ''的行 ⇒ 格子数与图数在结构上永远相等fbAttachPosters签名(recent, list)→(rows)(两个数组各写一遍循环,必只改一处)fbCardImagePaths并发取图(MAX_PARALLEL = 4):20 张串行实测 10~17s,卡片刷新窗口撑不住
核心思路不是"把 6 改成 20 就完事",而是让格子数由图数量决定:
这样哪怕某张海报 404、或某条本来就没
poster_key,也只是少一格,不会冒出一个淡紫占位块。
验证
groupV96()反向验证抓出的两个真问题(都已修)
CARD_IMG_MAX >= 4,而 bug 原值正好是 6,6 >= 4恒真 ⇒ 把 6 注回去守卫照样全绿。已改成> 6。7 条注入全部"红了 0 条"(看着像注入没生效)。已改成复制整个
entry/src/main。两条都已记入跨项目踩坑库(#462 / #463),避免同类问题再犯。