三、层叠布局容器(Stack)
堆叠容器组件 Stack
的布局方式是把子组件按照设置的对齐方式顺序依次堆叠,后一个子组件覆盖在前一个子组件上边。
注意:Stack
组件层叠式布局,尺寸较小的布局会有被遮挡的风险,
3.1.接口
堆叠容器组件 Stack接口如下:
Stack(value?: { alignContent?: Alignment })
3.2.参数
参数只有一个如下:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
alignContent | Alignment | 否 | 设置子组件在容器内的对齐方式。默认值:Alignment.Center |
参数alignContent,参数类型为Alignment枚举有9个参数值如下:
名称 | 描述 |
---|---|
TopStart | 子组件在 Stack 内靠左上角对齐 |
Top | 设置子组件在 Stack 内靠顶部水平居中对齐 |
TopEnd | 设置子组件在 Stack 内部靠右上角对齐 |
Start | 子组件靠 Stack 左边侧竖直居中对齐 |
Center | 设置子组件居中对齐 |
End | 设置子组件靠右竖直居中对齐 |
BottomStart | 设置子组件左下角对齐 |
Bottom | 设置子组件底部水平居中对齐 |
BottomEnd | 设置子组件右下角对齐 |
1)TopStart:子组件在 Stack
内靠左上角对齐,简单样例如下所示:
@Entry
@Component
struct StackExample {
build() {
//TopStart:子组件在 Stack 内靠左上角对齐,
Stack({alignContent:Alignment.TopStart}){
Text("test1")
.width(200) //宽
.height(180) //高
.textAlign(TextAlign.End)
.backgroundColor(Color.Orange) //设置背景
Text("test2")
.width(130) //宽
.height(100) //高
.textAlign(TextAlign.End)
.backgroundColor(Color.White) //设置背景
Text("test3")
.width(65) //宽
.height(55) //高
.textAlign(TextAlign.Center)
.backgroundColor(Color.Brown) //设置背景
}
.width("100%")
.height(200)
.backgroundColor(Color.Pink)
}
}
预览效果如下:
2)Top:设置子组件在 Stack
内靠顶部水平居中对齐,如下图所示:
3)TopEnd:设置子组件在 Stack
内部靠右上角对齐,如下所示:
4)Start:子组件靠 Stack
左边侧竖直居中对齐,如下所示:
5)Center(默认值):设置子组件居中对齐,如下所示:
6)End:设置子组件靠右竖直居中对齐,如下所示:
7)BottomStart:设置子组件左下角对齐,如下所示:
8)Bottom:设置子组件底部水平居中对齐,如下所示:
9)BottomEnd:设置子组件右下角对齐,如下所示:
3.3.属性
除支持通用属性外,还支持以下属性:
参数名 | 参数类型 | 参数描述 |
---|---|---|
alignContent | Alignment | 设置所有子组件在容器内的对齐方式。默认值:Alignment.Center从API version 9开始,该接口支持在ArkTS卡片中使用。说明:该属性与通用属性align同时设置时,后设置的属性生效。 |
alignContent:设置子组件的对齐方式,通过另一种方式设置而已, Alignment
的讲解同上,就没有在说明。
四、List容器
列表包含一系列相同宽度的列表项。适合连续、多行呈现同类数据,例如图片和文本。
说明
- 列表项ListItem是listItem的子项
- 列表项(ListItem)数量过多超出屏幕后,会自动提供滚动功能
- 列表项(ListItem)既可以纵向排列,也可以横向排列
- 注意list是容器,ListItem不是容器, ListItem中只能包含一个根组件,可以在里面先写一个跟组件,例如row容器,在编写内容
4.1.接口
接口说明:
List(value?:{space?: number | string, initialIndex?: number, scroller?: Scroller})
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
space | number | string | 否 | 子组件主轴方向的间隔。默认值:0说明:设置为除-1外其他负数或百分比时,按默认值显示。space参数值小于List分割线宽度时,子组件主轴方向的间隔取分割线宽度。 |
initialIndex | number | 否 | 设置当前List初次加载时视口起始位置显示的item的索引值。默认值:0说明:设置为除-1外其他负数或超过了当前List最后一个item的索引值时视为无效取值,无效取值按默认值显示。 |
scroller | Scroller | 否 | 可滚动组件的控制器。用于与可滚动组件进行绑定。说明:不允许和其他滚动类组件绑定同一个滚动控制对象。 |
4.2.属性
除支持通用属性外,还支持以下属性:
名称 | 参数类型 | 描述 |
---|---|---|
listDirection | Axis | 设置List组件排列方向。默认值:Axis.Vertical从API version 9开始,该接口支持在ArkTS卡片中使用。 |
divider | {strokeWidth: Length,color?:ResourceColor,startMargin?: Length,endMargin?: Length} | null | 设置ListItem分割线样式,默认无分割线。- strokeWidth: 分割线的线宽。- color: 分割线的颜色。- startMargin: 分割线与列表侧边起始端的距离。- endMargin: 分割线与列表侧边结束端的距离。从API version 9开始,该接口支持在ArkTS卡片中使用。endMargin +startMargin 不能超过列宽度。startMargin和endMargin不支持设置百分比。List的分割线画在主轴方向两个子组件之间,第一个子组件上方和最后一个子组件下方不会绘制分割线。多列模式下,ListItem与ListItem之间的分割线起始边距从每一列的交叉轴方向起始边开始计算,其他情况从List交叉轴方向起始边开始计算。 |
scrollBar | BarState | 设置滚动条状态。默认值:BarState.Off从API version 9开始,该接口支持在ArkTS卡片中使用。 |
cachedCount | number | 设置列表中ListItem/ListItemGroup的预加载数量,其中ListItemGroup将作为一个整体进行计算,ListItemGroup中的所有ListItem会一次性全部加载出来。具体使用可参考减少应用白块说明。默认值:1从API version 9开始,该接口支持在ArkTS卡片中使用。说明:单列模式下,会在List显示的ListItem前后各缓存cachedCount个ListItem。多列模式下, 会在List显示的ListItem前后各缓存cachedCount*列数个ListItem。 |
editMode(deprecated) | boolean | 声明当前List组件是否处于可编辑模式。可参考示例3实现删除选中的list项。从API version9开始废弃。默认值:false |
edgeEffect | EdgeEffect | 设置组件的滑动效果。默认值:EdgeEffect.Spring从API version 9开始,该接口支持在ArkTS卡片中使用。 |
chainAnimation | boolean | 设置当前List是否启用链式联动动效,开启后列表滑动以及顶部和底部拖拽时会有链式联动的效果。链式联动效果:List内的list-item间隔一定距离,在基本的滑动交互行为下,主动对象驱动从动对象进行联动,驱动效果遵循弹簧物理动效。默认值:false- false:不启用链式联动。- true:启用链式联动。从API version 9开始,该接口支持在ArkTS卡片中使用。 |
multiSelectable8+ | boolean | 是否开启鼠标框选。默认值:false- false:关闭框选。- true:开启框选。从API version 9开始,该接口支持在ArkTS卡片中使用。 |
lanes9+ | number | LengthConstrain | 以列模式为例(listDirection为Axis.Vertical):lanes用于决定List组件在交叉轴方向按几列布局。默认值:1规则如下:- lanes为指定的数量时,根据指定的数量与List组件的交叉轴尺寸除以列数作为列的宽度。- lanes设置了{minLength,maxLength}时,根据List组件的宽度自适应决定lanes数量(即列数),保证缩放过程中lane的宽度符合{minLength,maxLength}的限制。其中,minLength条件会被优先满足,即优先保证符合ListItem的交叉轴尺寸符合最小限制。- lanes设置了{minLength,maxLength},如果父组件交叉轴方向尺寸约束为无穷大时,固定按一列排列,列宽度按显示区域内最大的ListItem计算。- ListItemGroup在多列模式下也是独占一行,ListItemGroup中的ListItem按照List组件的lanes属性设置值来布局。- lanes设置了{minLength,maxLength}时,计算列数会按照ListItemGroup的交叉轴尺寸计算。当ListItemGroup交叉轴尺寸与List交叉轴尺寸不一致时ListItemGroup中的列数与List中的列数可能不一样。该接口支持在ArkTS卡片中使用。 |
alignListItem9+ | ListItemAlign | List交叉轴方向宽度大于ListItem交叉轴宽度 * lanes时,ListItem在List交叉轴方向的布局方式,默认为首部对齐。默认值:ListItemAlign.Start该接口支持在ArkTS卡片中使用。 |
sticky9+ | StickyStyle | 配合ListItemGroup组件使用,设置ListItemGroup中header和footer是否要吸顶或吸底。默认值:StickyStyle.None该接口支持在ArkTS卡片中使用。说明:sticky属性可以设置为 StickyStyle.Header | StickyStyle.Footer 以同时支持header吸顶和footer吸底。 |
ListItemAlign9+枚举说明:
名称 | 描述 |
---|---|
Start | ListItem在List中,交叉轴方向首部对齐。 |
Center | ListItem在List中,交叉轴方向居中对齐。 |
End | ListItem在List中,交叉轴方向尾部对齐。 |
StickyStyle9+枚举说明:
名称 | 描述 |
---|---|
None | ListItemGroup的header不吸顶,footer不吸底。 |
Header | ListItemGroup的header吸顶,footer不吸底。 |
Footer | ListItemGroup的footer吸底,header不吸顶。 |
说明
- List组件通用属性clip的默认值为true。
4.3.事件
名称 | 功能描述 |
---|---|
onItemDelete(deprecated)(event: (index: number) => boolean) | 当List组件在编辑模式时,点击ListItem右边出现的删除按钮时触发。从API version9开始废弃。- index: 被删除的列表项的索引值。 |
onScroll(event: (scrollOffset: number, scrollState: ScrollState) => void) | 列表滑动时触发。- scrollOffset: 每帧滚动的偏移量,List的内容向上滚动时偏移量为正,向下滚动时偏移量为负。- scrollState: 当前滑动状态。使用控制器调用ScrollEdge和ScrollToIndex时不会触发,其余情况有滚动就会触发该事件。从API version 9开始,该接口支持在ArkTS卡片中使用。 |
onScrollIndex(event: (start: number, end: number) => void) | 列表滑动时触发。计算索引值时,ListItemGroup作为一个整体占一个索引值,不计算ListItemGroup内部ListItem的索引值。- start: 滑动起始位置索引值。- end: 滑动结束位置索引值。触发该事件的条件:列表初始化时会触发一次,List显示区域内第一个子组件的索引值或后一个子组件的索引值有变化时会触发。List的边缘效果为弹簧效果时,在List划动到边缘继续划动和松手回弹过程不会触发onScrollIndex事件。从API version 9开始,该接口支持在ArkTS卡片中使用。 |
onReachStart(event: () => void) | 列表到达起始位置时触发。从API version 9开始,该接口支持在ArkTS卡片中使用。说明:List初始化时如果initialIndex为0会触发一次,List滚动到起始位置时触发一次。List边缘效果为弹簧效果时,划动经过起始位置时触发一次,回弹回起始位置时再触发一次。 |
onReachEnd(event: () => void) | 列表到底末尾位置时触发。从API version 9开始,该接口支持在ArkTS卡片中使用。说明:List边缘效果为弹簧效果时,划动经过末尾位置时触发一次,回弹回末尾位置时再触发一次。 |
onScrollFrameBegin9+(event: (offset: number, state: ScrollState) => { offsetRemain }) | 列表开始滑动时触发,事件参数传入即将发生的滑动量,事件处理函数中可根据应用场景计算实际需要的滑动量并作为事件处理函数的返回值返回,列表将按照返回值的实际滑动量进行滑动。- offset:即将发生的滑动量,单位vp。- state:当前滑动状态。- offsetRemain:实际滑动量,单位vp。触发该事件的条件:手指拖动List、List惯性划动时每帧开始时触发;List超出边缘回弹、使用滚动控制器的滚动不会触发。该接口支持在ArkTS卡片中使用。说明:当listDirection的值为Axis.Vertical时,返回垂直方向滑动量,当listDirection的值为Axis.Horizontal时,返回水平方向滑动量。 |
onScrollStart9+(event: () => void) | 列表滑动开始时触发。手指拖动列表或列表的滚动条触发的滑动开始时,会触发该事件。使用Scroller滑动控制器触发的带动画的滑动,动画开始时会触发该事件。该接口支持在ArkTS卡片中使用。 |
onScrollStop(event: () => void) | 列表滑动停止时触发。手拖动列表或列表的滚动条触发的滑动,手离开屏幕并且滑动停止时会触发该事件;使用Scroller滑动控制器触发的带动画的滑动,动画停止会触发该事件。从API version 9开始,该接口支持在ArkTS卡片中使用。 |
onItemMove(event: (from: number, to: number) => boolean) | 列表元素发生移动时触发。- from: 移动前索引值。- to: 移动后索引值。 |
onItemDragStart(event: (event: ItemDragInfo, itemIndex: number) => ((() => any) | void) | 开始拖拽列表元素时触发。- itemIndex: 被拖拽列表元素索引值。 |
onItemDragEnter(event: (event: ItemDragInfo) => void) | 拖拽进入列表元素范围内时触发。 |
onItemDragMove(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number) => void) | 拖拽在列表元素范围内移动时触发。- itemIndex: 拖拽起始位置。- insertIndex: 拖拽插入位置。 |
onItemDragLeave(event: (event: ItemDragInfo, itemIndex: number) => void) | 拖拽离开列表元素时触发。- itemIndex: 拖拽离开的列表元素索引值。 |
onItemDrop(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => void) | 绑定该事件的列表元素可作为拖拽释放目标,当在列表元素内停止拖拽时触发。- itemIndex: 拖拽起始位置。- insertIndex: 拖拽插入位置。- isSuccess: 是否成功释放。说明:跨List拖拽时,当拖拽释放的位置绑定了onItemDrop时会返回true,否则为false。List内部拖拽时,isSuccess为onItemMove事件的返回值。 |
ScrollState枚举说明:
名称 | 描述 |
---|---|
Idle | 未滑动状态。 |
Scroll | 手指拖动状态。 |
Fling | 惯性滑动状态。 |
说明:
要使List处于可编辑模式需配合onItemDelete事件和ListItem的editable属性,即可编辑模式实现删除列表项功能,需满足以下条件:
- editMode属性设置为true。
- 绑定onItemDelete事件,且事件回调返回true。
- ListItem的editable属性设置为true。
实现ListItem拖拽,需满足以下条件:
- editMode属性设置为true。
- 绑定onDragStart事件,且事件回调中返回浮动UI布局。
4.4.案例
4.4.1.示例一:
@Entry
@Component
struct ListItemExample {
private arr1:number[] = [0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20]
build() {
Column(){
List({space:20, initialIndex:0}){ //initialIndex设置当前List初次加载时视口起始位置显示的item的索引值。默认值:0
ForEach(this.arr1, (item)=>{
ListItem(){
Text('' + item)
.width("100%") //宽
.height(100) //高
.fontSize(16) //字体大小
.textAlign(TextAlign.Center) //居中显示
.borderRadius(10) //设置圆角半径
.backgroundColor("#A9A9A9") //设置背景颜色
}
// 结束ForEach循环,并且指定item作为每个列表项的标识符。
},item => item)
}
.listDirection(Axis.Vertical) //排列方向 Vertical垂直方向
/**
* 设置ListItem分割线样式,默认无分割线。
* - strokeWidth: 分割线的线宽。
* - color: 分割线的颜色。
* - startMargin: 分割线与列表侧边起始端的距离。
* - endMargin: 分割线与列表侧边结束端的距离。
*/
.divider({strokeWidth:2, color: "#8FBC8F", startMargin:20, endMargin:20})
.edgeEffect(EdgeEffect.Spring) //滑动到边缘无效果,默认值:EdgeEffect.Spring
/**
* 列表滑动时触发。
* - start: 滑动起始位置索引值。
* - end: 滑动结束位置索引值。
*/
.onScrollIndex((start: number,end:number) => {
console.info(`start: ${start}`)
console.info(`end: ${end}`)
})
.width("90%")
}
.width("100%")
.height("100%")
.backgroundColor("#808080")
.padding({top:10})
}
}
预览效果如下:
4.4.2.示例二
@Entry
@Component
struct ListLanesExample {
@State arr1:number[] = [0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20,21]
build() {
Column(){
List({space:20}){
ForEach(this.arr1, (item)=>{
ListItem(){
Text("hi:"+item)
.width("100%") //宽
.height(100) //高
.fontSize(16) //字体大小
.textAlign(TextAlign.Center) //居中显示
.borderRadius(10) //设置圆角半径
.backgroundColor("#A9A9A9") //设置背景颜色
}
.border({width:2, color: "#3CB371"})
//结束ForEach循环,并且指定item作为每个列表项的标识符。可以不写
},item=>item)
}
.height(360) //高
.width("100%") //宽
.border({width: 2, color: "#2F4F4F"}) //框线颜色和宽度
/*
* 设置了最小交叉轴长度为40vp,最大交叉轴长度为80vp,然后由于List没有设置listDirection,所以List的交叉轴方向为水平方向,
* 如果列表的交叉轴方向上的大小 小于最小交叉轴长度的两倍,那么只能展示一列。
* 如果将列表的交叉轴上的大小改成大于最小交叉轴长度的两倍,列数就会改变()。
*/
.lanes({minLength:40, maxLength:80})
//一种是直接传一个number,比如上面的列表我想让它交叉轴方向上展示三个子项
//.lanes(6)
/**
*
* List交叉轴方向宽度大于ListItem交叉轴宽度 * lanes时,ListItem在List交叉轴方向的布局方式,默认为首部对齐。
* 默认值:ListItemAlign.Start
*/
.alignListItem(ListItemAlign.Start)
}
.width("100%")
.height("100%")
.padding(20)
}
}
预览效果如下:
4.4.3.示例三
@Entry
@Component
struct ListExample02 {
@State arr1:number[] = [0,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16,17,18,19,20]
@State editFlag:boolean = false
build() {
//采用层叠布局(创建column容器、然后创建button按钮堆叠在column容器上面), TopStart子组件在 Stack 内靠左上角对齐
Stack({alignContent:Alignment.TopStart}){
//创建column容器
Column(){
List({space:20, initialIndex:0}){
ForEach(this.arr1, (item, index:number)=>{
ListItem(){
//弹性布局(水平排列),也可以采用row容器
Flex({direction:FlexDirection.Row}){
Text(""+item)
.width("100%")
.height(80)
.fontSize(20)
.textAlign(TextAlign.Center)
.borderRadius(10)
.backgroundColor("#F9F9FA")
.flexShrink(1)
if(this.editFlag){
//如果editFlag为true则显示按钮
Button(){
Text("delete").fontSize(16).borderRadius(5)
}
.width("30%")
.height(40)
.onClick(()=>{
//输出打印到控制太
console.info("删除元素:"+this.arr1[index])
//删除元素
this.arr1.splice(index,1)
//输出剩余元素
console.info(JSON.stringify(this.arr1))
//修改值为false
this.editFlag = false
//是否启用按钮按下时的切换效果。当状态设置为 false 时,切换效果无效。
}).stateEffect(true)
}
}
}
})
}.width("90%")
}.width("100%")
//创建button按钮
Button("edit list").onClick(()=>{
//点击修改修改值为true
this.editFlag = true
}).margin({top:5, left:20})
}
.size({height:"100%", width:"100%"})
.backgroundColor("#E1E1E1")
.padding({top:10})
}
}
预览效果如下:
4.4.4.示例四
章节一中的内容,商品如果比较多的时候无法下拉选择的,只能展示一行,这里采用List容器来解决这个问题,如下:
class Item{
//定位属性
name: string
image: any
price: number
constructor(name: string, image: any, price: number) {
this.name = name
this.image = image
this.price = price
}
}
@Entry
@Component
struct ListExample {
private items:Array<Item> = [
new Item("HUAWEI Mate 60 Pro",$r("app.media.mate60"), 7999.00),
new Item("HUAWEI MatePad Pro",$r("app.media.MatePadPro"),4299.00),
new Item("HUAWEI WATCH GT 4", $r("app.media.WATCHGT4"), 1538.00),
new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
new Item("HUAWEI FreeBuds Pro 3", $r("app.media.FreeBudsPro3"), 1499.00),
new Item("HUAWEI MateBook 14s", $r("app.media.MateBook14s"), 5299.00),
new Item("HUAWEI Mate 60",$r("app.media.mate60"), 5999.00),
new Item("HUAWEI MatePad2 Pro",$r("app.media.MatePadPro"),4599.00),
new Item("HUAWEI WATCH GT 5", $r("app.media.WATCHGT4"), 1638.00),
new Item("HUAWEI FreeBuds Pro 4", $r("app.media.FreeBudsPro3"), 1699.00),
]
build() {
Column({space:8}){
Row(){
Text("商品列表")
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.width("100%")
.padding(20)
List({space:10}){
ForEach(this.items,(item: Item)=>{
ListItem(){
Row({space:10}){
Image(item.image).width(100)
Column({space:4}){
Text(item.name).fontSize(15).fontWeight(FontWeight.Bold)
Text(`原价:¥ ${item.price}`).fontColor("#F36").fontSize(18)
}
.height("100%")
.alignItems(HorizontalAlign.Start)
}
.width("90%") //设置宽度
.height(120) //设置高度
.justifyContent(FlexAlign.SpaceBetween) //设置主轴方向主轴方向均匀分配弹性元素,相邻元素之间距离相同。 第一个元素与行首对齐,最后一个元素与行尾对齐。
.backgroundColor("#FFFFFF") //设置背景为白色
.borderRadius(10) //这是圆角班级
.padding(20) //内边距
}
})
}
.alignListItem(ListItemAlign.Center) //ListItem在List交叉轴方向的布局方式,默认为首部对齐。这里改为居中对齐
.height("100%")
.width("100%")
}
.width("100%")
.height("100%")
.backgroundColor("#D3D3D3")
}
}
预览效果如下: