ArkUI自定义Scroll滑动 指示器

时间:2024-10-22 09:02:13

最近项目中有个需求,关于滑动组件 Scroll 底部指示器跟随手势滑动等比例展示的效果,下图展示了要实现的效果。

自定义指示器组件

这里其实不是一个进度条,所以需要我们需要自定义绘制该组件,在鸿蒙中绘制组件单独使用,用于在页面上绘制指定的图形。有7种绘制类型,分别为Circle(圆形)、Ellipse(椭圆形)、Line(直线)、Polyline(折线)、Polygon(多边形)、Path(路径)、Rect(矩形),这里我么使用 Rect 进行矩形绘制,一个黑色的 Rect当做指示器的背景,蓝色当做指示器的进度。然后动态设置进度的左边距。

@Entry
@Component
export struct RectIndicator {

  @Prop marginLeft: number = 0 //indicator距离进度距离左边间距,默认是 0
  indicatorHeight: number = 20 //indicator的高度
  indicatorWidth: number = 200 //indicator的背景宽度
  indicatorProgressWidth: number = 160 //indicator 的进度宽度

  build() {
    Stack() {
      //绘制矩形背景
      Rect({ width: this.indicatorWidth, height: this.indicatorHeight })
        .radius(this.indicatorHeight / 2)
        .fill($r('app.color.bg_gray'))
        .stroke(Color.Transparent)

      //绘制矩形进度
      Rect({ width: this.indicatorProgressWidth, height: this.indicatorHeight })
        .radius(this.indicatorHeight / 2)
        .fill($r('app.color.main_color'))
        .margin({ left: this.marginLeft })
        .stroke(Color.Transparent)
    }.alignContent(Alignment.Start)
  }
}

添加 Scroll 监听

新建一个 RectIndicator 类,需要把两个矩形层叠在一起,所以外层使用Stack布局进行嵌套。调用该自定义组件的时候,可以设置 组件的宽度(indicatorWidth) ,高度(indicatorHeight),进度的宽度(indicatorProgressWidth)以及动态设置进度的左边距(marginLeft),可以通过监听 Scroll 的滑动事件来动态设置 marginLeft。

Scroll(this.scroll) {
   Row() {
     ForEach(SUB_MENUS, (item: Menu, index) => {
       Column() {
         Image(item.menuIcon).width(28).height(28)
         Text(item.menuText).fontSize(12).fontColor($r("app.color.text_two")).margin({ top: 5 })
       }.width("20%").id("item")
     })
   }
 }.scrollable(ScrollDirection.Horizontal)
 .margin({ top: 12 })
 .scrollBar(BarState.Off)
 //滑动监听
 .onDidScroll((_xOffset, _yOffset, scrollState) => {
   //当前状态为滑动状态
   if (scrollState == ScrollState.Scroll) {
     //获取滚动的偏移量,通过控制器获取比较准确
     const currentOffsetX = this.scroll.currentOffset().xOffset
     LogUtil.debug("滑动偏移量", vp2px(currentOffsetX).toString())
     //子组件宽度*2=未显示出来的组件/指示器灰色部分
     let ratio = this.itemWidth * 2 / 10
     //指示器进度的偏移量=scroll 的偏移量/比率
     this.indicatorLeft = vp2px(currentOffsetX) / ratio
   }
 })

onDidScroll 可以对滑动组件(包括但不限于 Scroll)设置监听,这里判断 scrollState 为滑动状态,获取当前滑动的偏移量 currentOffsetX,通过Scroll 的偏移量计算出 指示器的左间距(indicatorLeft)。

使用自定义RectIndicator组件

 RecIndicator({
          marginLeft: this.indicatorLeft, //左间距
          indicatorHeight: 4,  //指示器的高度
          indicatorWidth: 30,  //指示器的背景宽度
          indicatorProgressWidth: 20  //指示器进度的宽度
 }).margin({ top: 8, bottom: 8 })

使用方法:调用RecIndicator自定义组件,将高度,宽度等相关参数传递组件内,这里的进度宽度,可以通过 Scroll 组件长度计算出来,这里我就这只给一个宽度,不影响使用。

tips:indicatorLeft需要加一个@State 注解,保证组件可以根据indicatorLeft来实时刷新 UI。