HarmonyOS鸿蒙Next中如何解决Scroll组件无法设置某个元素一直固定在首位的问题

HarmonyOS鸿蒙Next中如何解决Scroll组件无法设置某个元素一直固定在首位的问题

【问题现象】

使用Scroll组件,某个元素无法在滑动时固定在首位,即如何实现吸顶效果。

【背景知识】

Scroll组件是容器组件,支持垂直和水平方向的滚动,可以实现组件内元素的前后滚动,让页面展示更多更丰富的内容。由于Scroll组件内仅支持一个子组件,一般搭配Column、Row、List、Grid等组件使用。

要实现Scroll组件的某个元素的吸顶效果,可以使用nestedScroll属性,对父子组件的向前、向后滑动设置嵌套滚动模式,实现组件与父组件的滚动联动。

【解决方案】

基于以上背景知识,吸顶效果可以通过对组件设置nestedScroll属性实现。通过改变参数的值,使父组件在向前滚动到边缘时触发边缘效果(固定在边缘)。一般而言,nestedScroll属性的参数值会设置为:

Scroll() {...}
  ......
  .nestedScroll({
    scrollForward: NestedScrollMode.PARENT_FIRST, // 向前滚动时父组件先滚动
    scrollBackward: NestedScrollMode.SELF_FIRST // 向后滚动时组件自身先滚动
  })

需要注意的是,nestedScroll是将组件与父组件进行嵌套,所以在实际开发中,要明确父组件的范围和实际效果。

以下分别以Tabs组件和List组件为例实现吸顶效果,并给出一个错误示例用于对比。

  • 实现Tabs组件的TabBar吸顶的效果

效果图:

点击放大

@Entry
@Component
struct ScrollCeiling1 {
  scroller: Scroller = new Scroller()
  itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  tabTitles: Array<string> = ['Tab1', 'Tab2', 'Tab3']

  // 创建Tabs组件单个tab下的内容组件,并设置TabContent组件内的List组件的nestedScroll属性,List组件的父组件为TabContent组件
  @Builder
  tabContentData(tabTitle: string) {
    TabContent() {
      List() {
        ForEach(this.itemData, (item: number) => {
          ListItem() {
            Text(`${ item }`).height(80).width('100%').textAlign(TextAlign.Center).backgroundColor(0xDDDDDD).margin({bottom: 5})
          }
        })
      }
      .nestedScroll({
        scrollForward: NestedScrollMode.PARENT_FIRST,
        scrollBackward: NestedScrollMode.SELF_FIRST
      })
    }.tabBar(tabTitle)
    .padding({top:5, bottom:5})
    .borderWidth(1)
    .borderColor(Color.Red)
  }

  /*
  设置scrollForward的滚动模式为NestedScrollMode.PARENT_FIRST:
  当控制List内元素向前滚动时,其父组件TabContent先滚动,覆盖Scroll组件嵌套的Column组件内的Image组件,随后Tabs组件触碰顶部边缘,触发边缘效果,从而固定在顶部
  设置scrollBackward的滚动模式为NestedScrollMode.SELF_FIRST:
  当控制List内元素向后滚动时,List的内容先滚动,直至滚动到List最顶部后,父组件TabContent开始滚动
   */

  build() {
    Scroll(this.scroller){
      Column() {
        Image($r('app.media.app_icon')).height(70)

        Tabs() {
          ForEach(this.tabTitles, (title: string) => {
            this.tabContentData(title)
          })
        }
        .borderWidth(2)
      }.width('90%')
      .alignItems(HorizontalAlign.Center)
    }.width('100%')
    .align(Alignment.Center)
    .scrollBar(BarState.Off)
  }
}
  • 实现List组件吸顶的效果

效果如图:

点击放大

@Entry
@Component
struct ScrollCeiling2 {
  scroller: Scroller = new Scroller()
  itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  classList: Array<string> = ['class1', 'class2']

  build() {
    Scroll(this.scroller) {
      Column({space : 10}) {
        // 搜索框
        Stack({alignContent: Alignment.End}) {
          Row() {
            Image($r('sys.media.ohos_ic_public_search_filled')).height(20)
              .margin({left:5})

            TextInput({placeholder: '请输入'}).type(InputType.Normal).fontSize('10fp').backgroundColor(Color.Transparent)
          }.height(50).width('100%')
          .borderWidth('1')
          .borderRadius(24)
          .padding({left:5, right:5, top:5, bottom:5})

          Button('搜索').type(ButtonType.Capsule).width(80).margin({right:5})
        }

        Column() {
          // Class_List
          List() {
            ForEach(this.classList, (cls: string) => {
              ListItem() {
                Text(cls).fontSize('20fp').width(100).textAlign(TextAlign.Center)
                  .borderWidth(1).borderRadius(12).margin({left:5, right:5})
              }
            })
          }.listDirection(Axis.Horizontal).height(30)

          /*
          设置scrollForward的滚动模式为NestedScrollMode.PARENT_FIRST:
          当控制Data_List内元素向前滚动时,其父组件Column先滚动,覆盖Scroll组件嵌套的Column组件内的Stack组件(搜索框),随后Column组件触碰顶部边缘,触发边缘效果,从而将Class_List固定在顶部
          设置scrollBackward的滚动模式为NestedScrollMode.SELF_FIRST:
          当控制Data_List内元素向后滚动时,Data_List的内容先滚动,直至滚动到Data_List最顶部后,父组件Column开始滚动
           */

          // Data_List
          List() {
            ForEach(this.itemData, (item: number) => {
              ListItem() {
                Text(`${ item }`).height(80).width('100%').textAlign(TextAlign.Center).backgroundColor(0xDDDDDD).margin({bottom: 5})
              }
            })
          }.height('90%')
          .nestedScroll({
            scrollForward: NestedScrollMode.PARENT_FIRST,
            scrollBackward: NestedScrollMode.SELF_FIRST
          })
        }.height('100%')
      }
    }.scrollBar(BarState.Off)
  }
}
  • 错误示例

效果如图:

点击放大

@Component
struct ScrollCeiling3 {
  scroller: Scroller = new Scroller()
  itemData: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  classList: Array<string> = ['class1', 'class2']

  build() {
    Scroll(this.scroller) {
      Column({space : 10}) {
        // 搜索框
        Stack({alignContent: Alignment.End}) {
          Row() {
            Image($r('sys.media.ohos_ic_public_search_filled')).height(20)
              .margin({left:5})

            TextInput({placeholder: '请输入'}).type(InputType.Normal).fontSize('10fp').backgroundColor(Color.Transparent)
          }.height(50).width('100%')
          .borderWidth('1')
          .borderRadius(24)
          .padding({left:5, right:5, top:5, bottom:5})

          Button('搜索').type(ButtonType.Capsule).width(80).margin({right:5})
        }

        // Class_List
        List() {
          ForEach(this.classList, (cls: string) => {
            ListItem() {
              Text(cls).fontSize('20fp').width(100).textAlign(TextAlign.Center)
                .borderWidth(1).borderRadius(12).margin({left:5, right:5})
            }
          })
        }.listDirection(Axis.Horizontal).height(30)

        /*
        Class_List和Data_List的外层没有另外嵌套一层Column组件,此时,Class_List和Data_List的父组件为Scroll嵌套的Column组件
        当控制Data_List内元素向前滚动时,基于nestedScroll的设置,父组件Column先滚动
        Column组件顶部已处于显示边缘,所以Column组件完成滚动,开始其子组件开始滚动,由于所有组件都在Column组件内,且Data_List的高度被设置为100%,所以Class_List会随着向前滚动而开始退出显示边缘,无法实现吸顶效果
         */
        // Data_List
        List() {
          ForEach(this.itemData, (item: number) => {
            ListItem() {
              Text(`${ item }`).height(80).width('100%').textAlign(TextAlign.Center).backgroundColor(0xDDDDDD).margin({bottom: 5})
            }
          })
        }.height('100%')
        .nestedScroll({
          scrollForward: NestedScrollMode.PARENT_FIRST,
          scrollBackward: NestedScrollMode.SELF_FIRST
        })
      }
    }.scrollBar(BarState.Off)
  }
}

有趣的是,假如将示例中的Data_List的高度设置为小于100%(比如93%),因为Data_List高度无法占满屏幕,Class_List会有部分露出甚至整个露出,可能出现与吸顶相似的效果:

点击放大

【总结】

将Scroll组件中的某个元素固定在首位,可以通过对组件设置nestedScroll属性,与正确的父组件绑定嵌套滚动模式,并将scrollForward参数设置为NestedScrollMode.PARENT_FIRST,将scrollBackward设置为NestedScrollMode.SELF_FIRST,确定向前滚动和向后滚动的滚动模式,从而实现Scroll组件内某个元素的吸顶效果。


更多关于HarmonyOS鸿蒙Next中如何解决Scroll组件无法设置某个元素一直固定在首位的问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中如何解决Scroll组件无法设置某个元素一直固定在首位的问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中实现Scroll组件的吸顶效果

在HarmonyOS鸿蒙Next中,要实现Scroll组件中某个元素固定在首位的吸顶效果,可以使用nestedScroll属性。通过设置scrollForwardNestedScrollMode.PARENT_FIRSTscrollBackwardNestedScrollMode.SELF_FIRST,确保父组件在向前滚动时先滚动,子组件在向后滚动时先滚动,从而实现吸顶效果。具体实现如下:

Scroll() {
  Column() {
    // 需要吸顶的元素
    Stack() {
      // 元素内容
    }
    .nestedScroll({
      scrollForward: NestedScrollMode.PARENT_FIRST,
      scrollBackward: NestedScrollMode.SELF_FIRST
    })

    // 其他内容
    List() {
      // 列表项
    }
  }
}

确保父组件和子组件的嵌套关系正确,避免错误示例中因父组件已处于显示边缘而无法实现吸顶效果的情况。

回到顶部