判断滚动与滑动容器组件的子组件是否可见
===================

#### 问题现象

使用List组件时，子组件在滑动过程中会滑出主页面，如何确认子组件当前是否可见？

onScrollStop(() => {
  _// 滚动结束后，判断是否需要播放新的视频（不是上一个索引&&上一个视频一半超出屏幕）_
  if (this.scrollIndex !== this.currentPlayIndex && this.theLastIsOutScreen) {
    this.currentPlayIndex = this.scrollIndex
    this.theLastIsOutScreen = false
    this.isShowPlay = true
  }

 _ // 需求，在这里判断index为currentPlayIndex+1的Image视图是否可见？_

})

#### 效果预览

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/ee/v3/O6oTJwvjRrWK0G2xfygo2g/zh-cn_image_0000002658791435.png?HW-CC-KV=V1&HW-CC-Date=20260709T134032Z&HW-CC-Expire=86400&HW-CC-Sign=A892E93132A0EE2AE865A330BBB35944D587062D348C0064A2507444AD239C6A "点击放大")

#### 背景知识

*   [getItemRect](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-container-scroll#getitemrect11)：方法获取子组件的大小及相对于容器组件的位置。支持Scroll、List、Grid、WaterFlow组件。其参数index必须是当前显示区域显示的子组件的索引值，否则视为非法值。非法值返回的大小和位置均为0。
*   [RectResult](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-types#rectresult10)：子组件的大小和相对于组件的位置。getItemRect方法的返回值对象。

#### 解决方案

通过getItemRect方法，获取RectResult对象，判断其属性x、y、width、height的值是否均不为0，否则不可见。

以List组件为例：

1.  VideoListPage页面。
    
    import { display } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct VideoListPage {
     _ // 图片资源需要开发者自行更换_
      private list = \[1, 2, 3, 4, 5, 6, 7, 8, 9, 10\];
     _ // 是否在播放_
      @State isShowPlay: boolean = false;
      @State screenHeight: number = 0;
      @State screenWidth: number = 0;
     _ // 记录当前播放的索引_
      @State currentPlayIndex: number = 0;
     _ // 列表当前滚动到的位置_
      @State scrollIndex: number = 0;
     _ // 上一个播放的视频是否一半已经超出屏幕_
      @State theLastIsOutScreen: boolean = false;
      mDirection: number = 0;
      playPosition: number | undefined = 0;
      private listScroller: Scroller = new Scroller();
    
      aboutToAppear(): void {
        this.currentPlayIndex = 2;
        this.isShowPlay = true;
    
        this.screenWidth = this.getUIContext().px2vp(display.getDefaultDisplaySync().width);
        this.screenHeight = this.getUIContext().px2vp(display.getDefaultDisplaySync().width) \* 9 / 16; // '30%';
      }
    
      build() {
        List({ scroller: this.listScroller }) {
          ForEach(this.list, (item: string, index: number) => {
            ListItem() {
              Stack() {
                if (this.isShowPlay && this.currentPlayIndex === index) {
                  VideoView()
                    .height(this.screenHeight)
                    .width(this.screenWidth)
                    .onVisibleAreaChange(\[0, 0.5\], (isVisible: boolean, currentRatio: number) => {
                      if (this.currentPlayIndex === index) {
                        console.info(\`isVisible: ${isVisible}\`);
                       _ // 视频的一半超出屏幕_
                        if (currentRatio <= 0.5) {
                          this.theLastIsOutScreen = true;
                        } else {
                          this.theLastIsOutScreen = false;
                        }
                      }
                    })
                } else {
                  Image($r('app.media.scrollDemo')) _// 图片资源需开发者自行更换_
                    .backgroundColor(Color.Gray)
                    .onClick(() => {
                      this.currentPlayIndex = index;
                      this.isShowPlay = true;
                      console.info(\`item: ${item}\`);
                    })
                    .height(this.screenHeight)
                    .width(this.screenWidth);
                }
              }
              .visibility(this.mDirection === 1 && this.currentPlayIndex !== index ? Visibility.None : Visibility.Visible);
            };
          });
        }
        .expandSafeArea(\[SafeAreaType.SYSTEM\], \[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM\])
        .onScrollIndex((start: number, end: number, center: number) => {
        _  // 记录滚动最后一次中间的位置_
          this.scrollIndex = center;
          console.info(\`start: ${start}\`);
          console.info(\`end: ${end}\`);
        })
        .onScrollStop(() => {
        _  // 滚动结束后，判断是否需要播放新的视频（不是上一个索引&&上一个视频一半超出屏幕）_
          if (this.scrollIndex !== this.currentPlayIndex && this.theLastIsOutScreen) {
            this.theLastIsOutScreen = false;
            this.isShowPlay = true;
          }
        _  // 需求，在这里判断index为currentPlayIndex+1的Image视图是否可见？_
          try {
            let rectResult = this.listScroller.getItemRect(this.currentPlayIndex + 1);
            console.info(\`RectResult.x：${rectResult.x}\`);
            console.info(\`RectResult.y：${rectResult.y}\`);
            console.info(\`RectResult.width：${rectResult.width}\`);
            console.info(\`RectResult.height：${rectResult.height}\`);
          } catch (error) {
            console.error(\`error: ${error}\`);
          }
        });
      }
    }
    
2.  VideoView页面。
    
    @Component
    export struct VideoView {
      _// 上下滑动手势，控制音量和亮度_
      private panOptionBrightAndVolume: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Vertical });
     _ // 左右滑动手势，控制快进快退_
      private panOptionSeek: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Horizontal });
    
      build() {
        Row()
          .gesture(GestureGroup(GestureMode.Exclusive,
            TapGesture({ count: 2 }).onAction((event: GestureEvent | undefined) => {
          _    // 双击_
              console.info(\`event: ${event}\`);
            }),
            TapGesture().onAction((event: GestureEvent | undefined) => {
         _     // 单击_
              console.info(\`event: ${event}\`);
            }),
            PanGesture(this.panOptionBrightAndVolume)
              .onActionStart((event: GestureEvent | undefined) => {
                console.info(\`event: ${event}\`);
              })
              .onActionUpdate((event: GestureEvent | undefined) => {
                console.info(\`event: ${event}\`);
              })
              .onActionEnd((event: GestureEvent | undefined) => {
                console.info(\`event: ${event}\`);
              }),
            PanGesture(this.panOptionSeek)
              .onActionStart((event: GestureEvent | undefined) => {
                console.info(\`event: ${event}\`);
              })
              .onActionUpdate((event: GestureEvent | undefined) => {
                console.info(\`event: ${event}\`);
              })
              .onActionEnd((event: GestureEvent | undefined) => {
                console.info(\`event: ${event}\`);
    
              })
          ))
          .onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => {
            console.info(\`event: ${event}\`);
            if (gestureInfo.type === GestureControl.GestureType.PAN\_GESTURE) {
         _     // 返回，REJECT，会使拖动手势失败_
              return GestureJudgeResult.REJECT;
            }
            return GestureJudgeResult.CONTINUE;
          })
          .height('100%')
          .width('100%')
          .backgroundColor(Color.White);
      }
    }
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/c1/v3/swXMlJqoS0-Fm7KMaD_RCw/zh-cn_image_0000002628552048.png?HW-CC-KV=V1&HW-CC-Date=20260709T134032Z&HW-CC-Expire=86400&HW-CC-Sign=B5FC4E0A408A94E89D10997C6DAAD33064727BBFDC696CDCCCE24D3A07015096 "点击放大")