HarmonyOS 鸿蒙Next 如何实现Scroll容器组件中子组件吸顶效果

发布于 1周前 作者 htzhanglong 最后一次编辑是 5天前 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 如何实现Scroll容器组件中子组件吸顶效果

【关键字】

Scroll / 子组件 / 吸顶

【问题描述】

垂直滑动的Scroll容器组件中,自上而下依次包含Text1、Text2、List三个子组件,然后上滑Scroll组件,滑至Text2子组件处,Text2吸顶,List子组件中内容可继续滑动。

请问此场景下Text2吸顶效果可如何实现?鸿蒙原生有提供吸顶效果的API吗?

【解决方案】

暂时没有原生的吸顶效果API,可以通过偏移量来实现相同效果。

具体Demo如下:

enum ScrollPosition {
start,
center,
end
}

class ItemClass {
content: string = '';
color: Color = Color.White;
}

@Entry
@Component
struct NestedScrollDemo {
@State listPosition: number = ScrollPosition.start; // 0代表滚动到List顶部,1代表中间值,2代表滚动到List底部。
@State scrollPosition: number = ScrollPosition.start; // 0代表滚动到页面顶部,1代表中间值,2代表滚动到页面底部。
@State showTitle: boolean = false;
@State currentYOffset: number = 0;
private arr: ItemClass[] = [];
private colorArr: Color[] = [Color.White, Color.Blue, Color.Brown, Color.Green, Color.Gray];
private scrollerForScroll: Scroller = new Scroller();
private scrollerForList: Scroller = new Scroller();
private scrollerForTitle: Scroller = new Scroller();
@State currentIndex: number = 0;

aboutToAppear() {
for (let i = 0; i < 6; i++) {
let data: ItemClass = {
content: i.toString(),
color: this.colorArr[i % 5]
}
this.arr.push(data);
}
}

@Builder
myBuilder() {
Row() {
List({ space: 2, initialIndex: 0, scroller: this.scrollerForTitle }) {
ForEach(this.arr, (item: ItemClass, index) => {
ListItem() {
Column() {
Text(item.content);
Divider()
.color('#000000')
.strokeWidth(5)
.visibility(index == this.currentIndex ? Visibility.Visible : Visibility.Hidden)
}
.width('25%')
.height(50)
.onClick(() => {
this.scrollerForList.scrollToIndex(index)
this.scrollerForScroll.scrollEdge(Edge.Bottom)
})
}
})
}
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
}
.backgroundColor('#ffe2d0d0')
.alignItems(VerticalAlign.Center)
}



build() {
Stack({ alignContent: Alignment.Top }) {
Scroll(this.scrollerForScroll) {
Column() {
Image($r('app.media.app_icon'))
.width("100%")
.height("40%")
this.myBuilder();

List({ space: 10, scroller: this.scrollerForList }) {
ForEach(this.arr, (item: ItemClass, index) => {
ListItem() {
Column() {
Text(item.content)
//添加其他内容
}
.width('100%')
.height(500)
.backgroundColor(item.color)
}.width("100%").height(500)
.onVisibleAreaChange([0.8], (isVisible) => {
if (isVisible) {
this.currentIndex = index;
this.scrollerForTitle.scrollToIndex(this.currentIndex);
}
})
}, (item: ItemClass) => item.content)
}
.padding({ left: 10, right: 10 })
.width("100%")
.edgeEffect(EdgeEffect.None)
.scrollBar(BarState.Off)
.onReachStart(() => {
this.listPosition = ScrollPosition.start
})
.onReachEnd(() => {
this.listPosition = ScrollPosition.end
})
.onScrollFrameBegin((offset: number, state: ScrollState) => {
// 滑动到列表中间时
if (!((this.listPosition == ScrollPosition.start && offset < 0)
|| (this.listPosition == ScrollPosition.end && offset > 0))) {
this.listPosition = ScrollPosition.center
}

// 如果页面已滚动到底部,列表不在顶部或列表有正向偏移量
if (this.scrollPosition == ScrollPosition.end
&& (this.listPosition != ScrollPosition.start || offset > 0)) {
return { offsetRemain: offset };
} else {
this.scrollerForScroll.scrollBy(0, offset)
return { offsetRemain: 0 };
}
})
.width("100%")
.height("calc(100% - 50vp)")
.backgroundColor('#F1F3F5')
}
}
.scrollBar(BarState.Off)
.width("100%")
.height("100%")
.onScroll((xOffset: number, yOffset: number) => {
this.currentYOffset = this.scrollerForScroll.currentOffset().yOffset;

// 非(页面在顶部或页面在底部),则页面在中间
if (!((this.scrollPosition == ScrollPosition.start && yOffset < 0)
|| (this.scrollPosition == ScrollPosition.end && yOffset > 0))) {
this.scrollPosition = ScrollPosition.center
}
})
.onScrollEdge((side: Edge) => {
if (side == Edge.Top) {
// 页面在顶部
this.scrollPosition = ScrollPosition.start
} else if (side == Edge.Bottom) {
// 页面在底部
this.scrollPosition = ScrollPosition.end
}
})
.onScrollFrameBegin(offset => {
if (this.scrollPosition == ScrollPosition.end) {
return { offsetRemain: 0 };
} else {
return { offsetRemain: offset };
}
})
}
.width('100%')
.height('100%')
.backgroundColor(0xDCDCDC)
}
}
3 回复

用 ListItemGroup 的自定义组头就能吧,也就是 Text1 是ListItem Text2 是LIstItemGroup 的头 
不知道是不是你要的效果,就是根组件不用 Scroll 用 List

[@Entry](/user/Entry)
[@Component](/user/Component)
struct Index {
   @Builder
   CustomHeader() {
       Text('2222222')
           .height(30)
           .backgroundColor(Color.Gray)
           .width('100%')
   }

   build() {
       List({
           space: 10
       }) {
           ListItem() {
               Text('111111')
                   .height(30)
                   .backgroundColor(Color.Gray)
                   .width('100%')
           }

           ListItemGroup({
               header: this.CustomHeader,
               space: 10
           }) {
               ForEach(Array.from({
                   length: 20
               }), (item: void, index: number) => {
                   ListItem() {
                       Text(index + '')
                           .height(30)
                           .backgroundColor('#f00')
                           .width('100%')
                   }
               })
           }
       }
       .width('100%')
       .height(200)
       .sticky(StickyStyle.Header)
   }
}

export default Index

cke_120.png

另一种实现吸顶的思路,两个嵌套的滑动控件,其中内部的List跟要吸顶的控件放到一个Stack中,List通过paddingTop空出吸顶控件的位置。 

在HarmonyOS鸿蒙Next系统中,实现Scroll容器组件中子组件吸顶效果,通常可以通过监听滚动事件并动态调整子组件的位置和属性来实现。以下是一个简要的技术思路:

  1. 使用Scroll组件:首先,确保你的布局中使用了Scroll组件,并包含需要吸顶的子组件。

  2. 监听滚动事件:通过监听Scroll组件的滚动事件(如onScroll),获取当前的滚动位置。

  3. 判断并设置吸顶:在滚动事件的回调中,判断子组件是否已滚动到顶部附近。如果是,则通过修改子组件的布局参数(如使用绝对定位),将其固定在屏幕顶部。

  4. 恢复子组件位置:当用户继续滚动,子组件需要恢复原来的位置时,通过监听滚动位置的变化,适时移除吸顶效果。

  5. 优化性能:在实现过程中,注意优化性能,避免频繁布局调整导致的性能问题。

实现这一功能可能需要结合具体的UI框架和布局方式,具体代码实现会有所不同。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。这样可以获得更专业的技术支持和解决方案。

回到顶部