组件可见性监听:onVisibleAreaChange
在鸿蒙ArkUI开发中,很多业务场景都需要知道组件有没有显示在屏幕上,比如广告是否展示、列表内容是否被用户看到,以此来做数据统计、曝光埋点等操作。而 onVisibleAreaChange 就是专门用来监听组件屏幕可见状态的核心事件,能精准捕捉组件可见面积的变化,是开发中非常实用的能力。
简单来说,这个事件的核心作用就是:实时监测组件在屏幕上的显示比例,判断组件是完全显示、部分显示,还是完全隐藏。
和单纯判断组件显示/隐藏不同,它能精准识别面积占比,最常用在广告曝光统计、列表项浏览记录统计、内容可见性追踪等精细化业务场景中。只要组件的屏幕可见面积发生变化,这个事件就会自动触发回调。
需要注意的是,该功能仅支持鸿蒙API 9及以上版本。
这个事件的使用方式很简单,直接在组件后链式调用即可,核心只有两个关键参数
1. 阈值数组 ratios
它是一组0-1之间的数字,用来设定触发监听的“临界值”。数值代表组件可见面积占自身总面积的比例。
比如我们最常用的 [0.0, 1.0],意思就是监听两个关键状态:组件从完全看不见、到完全看得见的整个变化过程。如果设置的数值超出0-1的范围,系统会自动修正为0或1,不用怕参数出错。
2. 回调函数 event
当组件可见面积达到设定阈值时,就会触发这个函数,自带两个核心参数:
- isVisible:布尔值,简单判断组件当前是否处于可见状态
- currentRatio:精准的可见比例,0代表完全看不见,1代表完全完整显示,小数则代表部分可见
onVisibleAreaChange 虽然好用,但有明确的使用范围,不注意就会出现统计不准、监听失效的问题:
- 只识别屏幕裁切,不识别遮挡:它只会计算组件是否滚出/进入屏幕,识别不到同级组件遮挡、弹窗遮挡、组件旋转、层级堆叠导致的隐藏,这类场景下监听结果会不准确。
- 仅支持正常挂载的UI组件:只有正常加载、挂载到页面UI树上的组件能被监听,预加载组件、悬浮弹窗类的自定义组件,无法触发这个事件。
- 不支持位移变换场景:组件因滚动导致的显示隐藏可以正常监听,但通过位移、偏移等特效改变位置导致的隐藏,无法识别。
- 数值存在微小误差:系统会自动对临界数值做近似处理,比如0.9997会直接判定为1(完全可见),误差不会超过0.001,不影响常规业务使用。
- 高版本可优化性能:API 18及以上版本新增了自定义计算间隔的配置,如果页面需要监听大量组件,可通过调整计算频率,降低设备功耗,避免页面卡顿。
示例:
@Entry
@Component
struct VisibleAreaDemo {
private scroller: Scroller = new Scroller()
private listArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8]
@State greenTextTip: string = '等待检测绿色组件状态'
@State yellowTextTip: string = '等待检测黄色组件状态'
build() {
Column() {
Column() {
Text(this.greenTextTip).fontSize(18).margin(5)
Text(this.yellowTextTip).fontSize(18).margin(5)
}
.width('100%')
.height(100)
.backgroundColor(0xF5F5F5)
// 滚动页面,核心监听区域
Scroll(this.scroller) {
Column({ space: 15 }) {
// 绿色测试组件:添加可见性监听
Text('我是可监听的绿色组件')
.width('90%')
.height(200)
.backgroundColor(0x34D399)
.fontSize(20)
.textAlign(TextAlign.Center)
// 监听:完全不可见(0.0)、完全可见(1.0)两个阈值
.onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, ratio: number) => {
// 组件完全展示在屏幕中,可做曝光埋点上报
if (isVisible && ratio >= 1.0) {
this.greenTextTip = '✅ 绿色组件:完全可见,已曝光';
console.log('绿色组件曝光成功');
}
// 组件完全离开屏幕
if (!isVisible && ratio <= 0.0) {
this.greenTextTip = '❌ 绿色组件:完全隐藏';
}
})
// 黄色测试组件:添加可见性监听
Row() {
Text('我是可监听的黄色组件')
.fontSize(20)
}
.width('90%')
.height(200)
.backgroundColor(0xFBBF24)
.justifyContent(FlexAlign.Center)
.onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, ratio: number) => {
if (isVisible && ratio >= 1.0) {
this.yellowTextTip = '✅ 黄色组件:完全可见,已曝光';
console.log('黄色组件曝光成功');
}
if (!isVisible && ratio <= 0.0) {
this.yellowTextTip = '❌ 黄色组件:完全隐藏';
}
})
// 填充列表,方便上下滚动测试效果
ForEach(this.listArr, (item: number) => {
Text(`列表条目${item}`)
.width('90%')
.height(120)
.backgroundColor(Color.White)
.borderRadius(12)
.textAlign(TextAlign.Center)
.fontSize(16)
}, (item) => item.toString())
}
.width('100%')
.padding(10)
}
.layoutWeight(1)
.backgroundColor(0xE5E7EB)
}
.width('100%')
.height('100%')
}
}更多推荐

所有评论(0)