这是一个鸿蒙(HarmonyOS / RNOH)平台上非常典型的问题。我来分析根因。
核心问题
在鸿蒙手机上,measure 和 measureInWindow 返回的 y / pageY 在滑动组件后值不变,是因为这两个 API 在鸿蒙的 RNOH Fabric 渲染器下,返回的是组件相对于其父容器(或布局树)的静态布局坐标,而不是相对于屏幕视口的实时坐标。
两个 API 的语义差异
在 iOS/Android 上:
measure(callback) → 回调参数 (x, y, width, height, pageX, pageY),其中 pageX/pageY 是相对于根视图的绝对坐标,会随滚动变化
measureInWindow(callback) → 回调参数 (x, y, width, height),其中 x/y 是相对于屏幕窗口的坐标,也会随滚动变化
在鸿蒙上:
这两个 API 底层走的是 ArkUI 的布局系统,返回的是组件在布局树中的静态位置,滚动容器的 offset 没有被减去,所以滑动后值不变。
你代码中的具体表现
看你的 checkSelectedVisble 函数:
// 这里 measure 拿到的 pageY 在鸿蒙上不会随滚动变化
currentRef.measure((x, y, width, height, pageX, pageY) => {
console.log('fwc=====> measure', x, y, width, height, pageX, pageY);
});
// 这里 measureInWindow 拿到的 y 在鸿蒙上同样不会随滚动变化
currentRef.measureInWindow((x, y, width, height) => {
console.log('=====> measureInWindow', x, y, width, height);
});
滑动右侧 List 后,CheckBox 在屏幕上的视觉位置变了,但 y 值不变 → 导致 currentBottomY 计算错误 → "上滑查看更多" 的显示/隐藏逻辑失效。
你已经写了正确的兜底方案
你的 measureInWindowCompat 函数在鸿蒙上走了 UIManager.measure → UIManager.measureInWindow → view.measureInWindow 的降级链,但这三个在鸿蒙上本质都有同样的问题。
而在这段代码其实是正确的思路:
// 使用统一坐标系:窗口坐标(top of list) + 内容坐标(maxY) - 内容滚动偏移(offset)
measureInWindowCompat(
rightListInstance as unknown as View,
({ y }) => {
const currentBottomY = y + maxY - rightScrollOffsetRef.current;
updateSelectedVisibility(currentBottomY);
}
);
这个方案不依赖子元素的实时 measure,而是用:
List 容器的窗口 y(相对稳定,只受外层面板滚动影响)
maxY(子元素在 List 内容中的布局偏移,静态值)
rightScrollOffsetRef.current(List 的实时滚动偏移)
手动计算出真实的屏幕坐标。这个公式在鸿蒙上是可靠的。
建议修复
在鸿蒙上,直接跳过对 CheckBox 的 measure / measureInWindow 调用,统一走你已有的 List 容器坐标 + 内容偏移的计算方式:
// 统一坐标系方案(鸿蒙和其他平台都可靠)
关键点:把 rightListInstance 的测量逻辑提到 currentRef.measureInWindow 之前,让它优先执行。因为 List 容器本身不在滚动内容里(它是滚动容器),所以它的 measureInWindow 返回值即使在鸿蒙上也是相对稳定的,再配合手动减去 rightScrollOffsetRef.current 就能得到正确的屏幕坐标。
简单说:鸿蒙上别信子元素的 measure 结果,用容器坐标 + 内容偏移自己算。