在鸿蒙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%')
  }
}
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐