HarmonyOS鸿蒙Next中无论Column高度是否超过Scroll,我们都希望Column的起始位置总是从Scroll的顶部开始显示(居上显示)。如何实现这种效果?
HarmonyOS鸿蒙Next中无论Column高度是否超过Scroll,我们都希望Column的起始位置总是从Scroll的顶部开始显示(居上显示)。如何实现这种效果? 在鸿蒙ArkUI的Scroll组件中,如果只包含一个Column子组件时,需要Column的高度超过Scroll本身才可滚动。
-
通常情况:Column的高度由其内部内容决定。内容多则Column高(大于Scroll),会可滚动;内容少则Column矮(小于Scroll),不会滚动。这本身符合预期。
-
当前问题:当Column高度不足(内容少)时,Column会在Scroll的可视区域内垂直居中显示。无法顶部对齐(居上显示),Scroll无法设置子组件的对齐方式。
-
需求:无论Column高度是否超过Scroll,我们都希望Column的起始位置总是从Scroll的顶部开始显示(居上显示)。如何实现这种效果?
更多关于HarmonyOS鸿蒙Next中无论Column高度是否超过Scroll,我们都希望Column的起始位置总是从Scroll的顶部开始显示(居上显示)。如何实现这种效果?的实战教程也可以访问 https://www.itying.com/category-93-b0.html
对 Column
使用以下属性设置约束尺寸,组件布局时,进行尺寸范围限制。
.constraintSize({ minHeight: '100%', maxHeight: Infinity })
对 Scroll
使用以下属性可设置其子组件的对齐方式为顶对齐
.align(Alignment.Top)
更多关于HarmonyOS鸿蒙Next中无论Column高度是否超过Scroll,我们都希望Column的起始位置总是从Scroll的顶部开始显示(居上显示)。如何实现这种效果?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
【背景知识】
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']
@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组件吸顶的效果
@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中,要实现Column始终从Scroll顶部开始显示,可以使用Scroll的alignContent属性。将alignContent设置为FlexAlign.Start即可强制内容顶部对齐。示例代码如下:
Scroll() {
Column() {
// 内容组件
}
.width('100%')
}
.scrollable(ScrollDirection.Vertical)
.alignContent(FlexAlign.Start)
.width('100%')
.height('100%')
该方法适用于API 9及以上版本,无需考虑Column内容高度。
在HarmonyOS Next中实现Column始终从Scroll顶部开始显示的效果,可以通过以下方式解决:
- 使用Stack布局包裹Scroll和Column,并设置Column的对齐方式:
Stack({ alignContent: Alignment.Top }) {
Scroll() {
Column() {
// 内容组件
}
.width('100%')
}
}
.width('100%')
.height('100%')
- 另一种方法是给Column设置alignItems属性:
Scroll() {
Column() {
// 内容组件
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
- 如果内容可能动态变化,可以强制设置Column的最小高度:
Scroll() {
Column() {
// 内容组件
}
.width('100%')
.minHeight(Scroll的高度值)
}
这些方法都能确保Column内容始终从Scroll顶部开始显示,而不受内容多少影响。第一种Stack方案是最可靠的实现方式。