DEV Community

HarmonyOS
HarmonyOS

Posted on

Determine Whether Subcomponents of the Scroll or Sliding Container Component Are Visible

Read the original article:Determine Whether Subcomponents of the Scroll or Sliding Container Component Are Visible

Requirement Description

When using the Scroll component, subcomponents slide out of the main page during scrolling. How can we determine whether a subcomponent is currently visible?

onScrollStop(() => {
  // After scrolling stops, determine whether a new video needs to play 
  // (not the previous index && the previous video is halfway off-screen)
  if (this.scrollIndex !== this.currentPlayIndex && this.theLastIsOutScreen) {
    this.currentPlayIndex = this.scrollIndex
    this.theLastIsOutScreen = false
    this.isShowPlay = true
  }

  // Requirement: Here, determine whether the Image view with index = currentPlayIndex + 1 is visible?

})
Enter fullscreen mode Exit fullscreen mode

Background Knowledge

This method obtains the size and position of a subcomponent relative to the container component.

Supported components include Scroll, List, Grid, and WaterFlow.

The parameter index must be the index of a subcomponent displayed in the current visible area; otherwise, it is considered invalid.

For invalid values, both the size and position returned are 0.

Represents the size and position of a subcomponent relative to the component. It is the return object of the getItemRect method.

Implementation Steps

Use the getItemRect method to obtain a RectResult object and check whether its attributes x, y, width, and height are all non-zero; otherwise, the subcomponent is not visible.

Code Snippet

For example, using the Scroll component, the code is as follows:

  • Index.ets
import { display } from '@kit.ArkUI'
import { VideoView } from './VideoView'

@Entry
@Component
struct VideoListPage {
  // Image resources need to be replaced by the developer
  private list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  // Whether video is playing
  @State isShowPlay: boolean = false
  @State screenHeight: number = 0
  @State screenWidth: number = 0
  // Record the index of the currently playing video
  @State currentPlayIndex: number = 0;
  // The current scroll position of the list
  @State scrollIndex: number = 0;
  // Whether the previously played video is half off-screen
  @State theLastIsOutScreen: boolean = false;
  @State mDirection: number = 0;
  playPosition: number | undefined = 0
  @State videoUrl: string = ''
  private listScroller: Scroller = new Scroller();

  aboutToAppear(): void {
    // Set log level: DEBUG, INFO, WARNING, ERROR.  
    // If not called, logs are not enabled.
    // Start playing from the 2nd item
    this.currentPlayIndex = 2
    this.isShowPlay = true

    this.screenWidth = this.getUIContext().px2vp(display.getDefaultDisplaySync().width)
    this.screenHeight = this.getUIContext().px2vp(display.getDefaultDisplaySync().width) * 9 / 16
  }

  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) {
                    // Video is halfway off-screen
                    if (currentRatio <= 0.5) {
                      this.theLastIsOutScreen = true
                    } else {
                      this.theLastIsOutScreen = false
                    }
                  }
                })
                .backgroundColor(Color.Black)
            } else {
              Image($r('app.media.drag_and_exchange_ic_public_app3')) // Image resource must be replaced by the developer
                .backgroundColor(Color.Gray)
                .onClick(() => {
                  this.currentPlayIndex = index
                  this.isShowPlay = true
                })
                .height(this.screenHeight)
                .width(this.screenWidth)
            }
          }
          // 1. Implement full-screen feature:
          // To implement full-screen, click the full-screen button.
          // This will expand the video component height to 100% of the screen.
          // You can also hide other list items and display only the current video item.
          .visibility(this.mDirection === 1 && this.currentPlayIndex !== index ? Visibility.None : Visibility.Visible)
        }
      })
    }.onScrollIndex((start: number, end: number, center: number) => {
      // Record the last center position during scrolling
      this.scrollIndex = center
    })
    .onScrollStop(() => {
      // After scrolling stops, determine whether to play a new video 
      // (not the previous index && previous video is half off-screen)
      if (this.scrollIndex !== this.currentPlayIndex && this.theLastIsOutScreen) {
        this.currentPlayIndex = this.scrollIndex
        this.theLastIsOutScreen = false
        this.isShowPlay = true
      }
      // Requirement: Here, determine whether the Image view with index = currentPlayIndex + 1 is visible?
      let rectResult = this.listScroller.getItemRect(this.currentPlayIndex + 1)
      console.info('hm-->' + 'RectResult.x:' + rectResult.x, 'RectResult.y:' + rectResult.y,
        'RectResult.width:' + rectResult.width, 'RectResult.height:' + rectResult.height)
    })
  }
}
Enter fullscreen mode Exit fullscreen mode
  • VideoView.ets
@Component
export struct VideoView {
  // Vertical swipe gesture controls brightness and volume
  private panOptionBrightAndVolume: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Vertical });
  // Horizontal swipe gesture controls fast-forward and rewind
  private panOptionSeek: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Horizontal });

  build() {
    Row()
      .gesture(GestureGroup(GestureMode.Exclusive,
        TapGesture({ count: 2 }).onAction((event: GestureEvent | undefined) => {
          // Double tap
        }),
        TapGesture().onAction((event: GestureEvent | undefined) => {
          // Single tap
        }),
        PanGesture(this.panOptionBrightAndVolume)
          .onActionStart((event: GestureEvent | undefined) => {
          })
          .onActionUpdate((event: GestureEvent | undefined) => {
          })
          .onActionEnd((event: GestureEvent | undefined) => {
          }),
        PanGesture(this.panOptionSeek)
          .onActionStart((event: GestureEvent | undefined) => {
          })
          .onActionUpdate((event: GestureEvent | undefined) => {
          })
          .onActionEnd((event: GestureEvent | undefined) => {
          })

      ))
      .onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent) => {
        if (gestureInfo.type === GestureControl.GestureType.PAN_GESTURE) {
          // Return REJECT to cancel the drag gesture
          return GestureJudgeResult.REJECT;
        }
        return GestureJudgeResult.CONTINUE;
      })
      .height('100%')
      .width('100%')
      .backgroundColor(Color.White)
  }
}
Enter fullscreen mode Exit fullscreen mode

Test Results

cke_1957.gif

I hm-->RectResult.x:0 RectResult.y:205.29904174804688 RectResult.width:233 RectResult.height:131.0625

I hm-->RectResult.x:0 RectResult.y:132.17404174804688 RectResult.width:233 RectResult.height:131.0625

Limitations or Considerations

This example supports API Version 19 Release and above.

This example supports HarmonyOS 5.1.1 Release SDK and above.

This example must be compiled and run using DevEco Studio 5.1.1 Release or later.

Written by Bunyamin Akcay

Top comments (0)