布局
线性布局 (Row/Column)
线性容器Row和Column构建,Column容器内子元素按照垂直方向排列,Row容器内子元素按照水平方向排列。
在布局容器内,可以通过space属性设置排列方向上子元素的间距,使各子元素在排列方向上有等间距效果。
可以通过alignItems属性设置子元素在交叉轴(排列方向的垂直方向)上的对齐方式。
1.alignItems(HorizontalAlign.Start)子元素在水平方向左对齐。
2.alignItems(HorizontalAlign.Center)子元素在水平方向居中对齐。
3.alignItems(HorizontalAlign.End)子元素在水平方向右对齐。
4.alignItems(VerticalAlign.Top)子元素在垂直方向顶部对齐。
5.alignItems(VerticalAlign.Center)子元素在垂直方向居中对齐。
6.alignItems(VerticalAlign.Bottom)子元素在垂直方向底部对齐。
可以通过justifyContent属性设置子元素在容器主轴上的排列方式。
1.justifyContent(FlexAlign.Start):元素在首端对齐,第一个元素与行首对齐,同时后续的元素与前一个对齐。
2.justifyContent(FlexAlign.Center):元素在中心对齐,第一个元素与行首的距离与最后一个元素与行尾距离相同。
3.justifyContent(FlexAlign.End):元素在尾部对齐,最后一个元素与行尾对齐,其他元素与后一个对齐。
4.justifyContent(FlexAlign.SpaceBetween):均匀分配元素,相邻元素之间距离相同。第一个元素与行首对齐,最后一个元素与行尾对齐。
5.justifyContent(FlexAlign.SpaceAround):均匀分配元素,相邻元素之间距离相同。第一个元素到行首的距离和最后一个元素到行尾的距离是相邻元素之间距离的一半。
6.justifyContent(FlexAlign.SpaceEvenly):均匀分配元素,相邻元素之间的距离、第一个元素与行首的间距、最后一个元素到行尾的间距都完全一样。
自适应拉伸
在线性布局下,常用空白填充组件Blank,在容器主轴方向自动填充空白空间,达到自适应拉伸效果。
自适应延伸
自适应延伸是指在不同尺寸设备下,当页面的内容超出屏幕大小而无法完全显示时,可以通过滚动条进行拖动展示
1.在List中添加滚动条
2.使用Scroll组件
.scrollable(ScrollDirection.Vertical) // 滚动方向为垂直方向
.scrollBar(BarState.On) // 滚动条常驻显示
.scrollBarColor(Color.Gray) // 滚动条颜色
.scrollBarWidth(10) // 滚动条宽度
.edgeEffect(EdgeEffect.Spring) // 滚动到边沿后回弹
层叠布局 (Stack)
层叠布局(StackLayout)用于在屏幕上预留一块区域来显示组件中的元素,提供元素可以重叠的布局
Stack组件为容器组件,容器内可包含各种子元素。其中子元素默认进行居中堆叠。
Stack组件通过alignContent参数实现位置的相对移动。
Stack({alignContent:Alignment.TopStart}){//左上角
Text('第一个子元素').width('80%').height(260).backgroundColor('#faf')
.zIndex(100)
Text('第2个子元素').width('60%').height(230).backgroundColor('#fcf')
.zIndex(101)
Text('第3个子元素').width('40%').height(200).backgroundColor('#fef')
.zIndex(102)
}
.width('90%')
.height(300)
.backgroundColor('gray')
弹性布局 (Flex)
弹性布局(Flex)提供更加有效的方式对容器中的子元素进行排列、对齐和分配剩余空间。
概念
主轴:Flex组件布局方向的轴线,子元素默认沿着主轴排列。主轴开始的位置称为主轴起始点,结束位置称为主轴结束点。
交叉轴:垂直于主轴方向的轴线。交叉轴开始的位置称为交叉轴起始点,结束位置称为交叉轴结束点。
布局方向
通过设置参数direction,可以决定主轴的方向,从而控制子元素的排列方向。
1.direction: FlexDirection.Row主轴为水平方向,子元素从起始端沿着水平方向开始排布。
2.direction: FlexDirection.RowReverse主轴为水平方向,子元素从终点端沿着FlexDirection. Row相反的方向开始排布。
3.direction: FlexDirection.Column主轴为垂直方向,子元素从起始端沿着垂直方向开始排布。
4.direction: FlexDirection.ColumnReverse 主轴为垂直方向,子元素从终点端沿着FlexDirection. Column相反的方向开始排布。
布局换行
wrap属性控制当子元素主轴尺寸之和大于容器主轴尺寸时,Flex是单行布局还是多行布局。
1.wrap: FlexWrap.NoWrap 不换行。如果子元素的宽度总和大于父元素的宽度,则子元素会被压缩宽度。
2.wrap: FlexWrap.Wrap换行,每一行子元素按照主轴方向排列。
3.wrap: FlexWrap.WrapReverse换行,每一行子元素按照主轴反方向排列。
主轴对齐方式
通过justifyContent参数设置子元素在主轴方向的对齐方式。
1.justifyContent: FlexAlign.Start子元素在主轴方向起始端对齐, 第一个子元素与父元素边沿对齐,其他元素与前一个元素对齐。
2.justifyContent: FlexAlign.Center子元素在主轴方向居中对齐。
3.justifyContent: FlexAlign.End 子元素在主轴方向终点端对齐, 最后一个子元素与父元素边沿对齐,其他元素与后一个元素对齐。
4.justifyContent: FlexAlign.SpaceBetweenFlex主轴方向均匀分配弹性元素,相邻子元素之间距离相同。第一个子元素和最后一个子元素与父元素边沿对齐。
5.justifyContent: FlexAlign.SpaceAroundFlex主轴方向均匀分配弹性元素,相邻子元素之间距离相同。第一个子元素到主轴起始端的距离和最后一个子元素到主轴终点端的距离是相邻元素之间距离的一半。
6.justifyContent: FlexAlign.SpaceEvenlyFlex主轴方向元素等间距布局,相邻子元素之间的间距、第一个子元素与主轴起始端的间距、最后一个子元素到主轴终点端的间距均相等。
交叉轴对齐
可以通过Flex组件的alignItems参数设置子元素在交叉轴的对齐方式。
1.alignItems: ItemAlign.Auto 使用Flex容器中默认配置。
2.alignItems: ItemAlign.Start交叉轴方向首部对齐。
3.alignItems: ItemAlign.Center 交叉轴方向居中对齐。
4.alignItems: ItemAlign.End交叉轴方向底部对齐。
5.alignItems: ItemAlign.Stretch 交叉轴方向拉伸填充,在未设置尺寸时,拉伸到容器尺寸。
6.alignItems: ItemAlign.Baseline 交叉轴方向文本基线对齐。
子元素的alignSelf属性也可以设置子元素在父容器交叉轴的对齐格式,且会覆盖Flex布局容器中alignItems配置。
内容对齐
可以通过alignContent参数设置子元素各行在交叉轴剩余空间内的对齐方式,只在多行的Flex布局中生效
1.alignContent: FlexAlign.Start子元素各行与交叉轴起点对齐。
2.alignContent: FlexAlign.Center子元素各行在交叉轴方向居中对齐。
3.alignContent: FlexAlign.End子元素各行与交叉轴终点对齐。
4.alignContent: FlexAlign.SpaceBetween子元素各行与交叉轴两端对齐,各行间垂直间距平均分布。
5.alignContent: FlexAlign.SpaceAround子元素各行间距相等,是元素首尾行与交叉轴两端距离的两倍。
6.alignContent: FlexAlign.SpaceEvenly 子元素各行间距,子元素首尾行与交叉轴两端距离都相等。
自适应拉伸
在弹性布局父组件尺寸过小时,通过子元素的以下属性设置其在父容器的占比,达到自适应布局。
1.flexBasis:设置子元素在父容器主轴方向上的基准尺寸。
.flexBasis('auto')
2.flexGrow:设置父容器的剩余空间分配给此属性所在组件的比例。用于分配父组件的剩余空间。
.flexGrow(2)
相对布局 (RelativeContainer)
RelativeContainer为采用相对布局的容器,支持容器内部的子元素设置相对位置关系,适用于界面复杂场景的情况,对多个子组件进行对齐和排列。子元素支持指定兄弟元素作为锚点,也支持指定父容器作为锚点,基于锚点做相对位置布局。
概念
锚点:通过锚点设置当前元素基于哪个元素确定位置。
对齐方式:通过对齐方式,设置当前元素是基于锚点的上中下对齐,还是基于锚点的左中右对齐。
锚点设置
在水平方向上,可以设置left、middle、right的锚点。在竖直方向上,可以设置top、center、bottom的锚点。
ID默认为“container”,其余子元素的ID通过id属性设置。
//相对布局
RelativeContainer(){
Row(){}.height(100).width(100).backgroundColor('blue')
//alignRules:定位规则
.alignRules({//anchor锚点
'bottom':{'anchor':'__container__',align:VerticalAlign.Bottom},
'right':{'anchor':'__container__',align:HorizontalAlign.End}
})
Row(){}.height(100).width(100).backgroundColor('red')
.alignRules({
'top':{'anchor':'__container__',align:VerticalAlign.Top},
'left':{'anchor':'__container__',align:HorizontalAlign.Start}
})
Row(){}.height(100).width(100).backgroundColor('red')
.alignRules({//正中间
'center':{'anchor':'__container__',align:VerticalAlign.Center},
'middle':{'anchor':'__container__',align:HorizontalAlign.Center}
})
}.width('100%').height(300).backgroundColor('gray')
.id('__container__')//系统默认生成
以兄弟元素为锚点。
RelativeContainer(){
Row().height(100).width(100).backgroundColor('red').id('r1')
.alignRules({
'middle':{'anchor':'__container__',align:HorizontalAlign.Center}
})
Row().height(100).width(100).backgroundColor('blue').id('r2')//锚点
.alignRules({//HorizontalAlign:水平
'left':{'anchor':'r1',align:HorizontalAlign.End}
})
Row().height(100).width(100).backgroundColor('blue')
.alignRules({//VerticalAlign:垂直
'top':{'anchor':'r1',align:VerticalAlign.Bottom},
'left':{'anchor':'r1',align:HorizontalAlign.Start}
})
Row().height(100).width(100).backgroundColor('green')
.alignRules({//VerticalAlign:垂直
'top':{'anchor':'r2',align:VerticalAlign.Bottom},
'left':{'anchor':'r2',align:HorizontalAlign.Start}
})
}.width('100%').height(300).backgroundColor('#faf')
设置相对于锚点的对齐位置
设置了锚点之后,可以通过align设置相对于锚点的对齐位置。
在水平方向上,对齐位置可以设置为HorizontalAlign.Start、HorizontalAlign.Center、HorizontalAlign.End。
在竖直方向上,对齐位置可以设置为VerticalAlign.Top、VerticalAlign.Center、VerticalAlign.Bottom。
子组件位置偏移
子组件经过相对位置对齐后,位置可能还不是目标位置,开发者可根据需要进行额外偏移设置offset。
Row().height(100).width(100).backgroundColor('#ccc')
.alignRules({//VerticalAlign:垂直
'top':{'anchor':'r2',align:VerticalAlign.Bottom},
'left':{'anchor':'r2',align:HorizontalAlign.Start}
})
.offset({
x:-50,
y:-50
})
栅格布局 (GridRow/GridCol)
栅格布局是一种通用的辅助定位工具,对移动设备的界面设计有较好的借鉴作用。
GridRow为栅格容器组件,需与栅格子组件GridCol在栅格布局场景中联合使用。
栅格系统断点
栅格系统以设备的水平宽度作为断点依据,定义设备的宽度类型,形成了一套断点规则。
栅格系统默认断点将设备宽度分为xs、sm、md、lg四类
xs:最小宽度类型设备。
sm:小宽度类型设备
md:中等宽度类型设备。
lg:大宽度类型设备。
//栅格布局
GridRow() {
GridCol({span:{
xs:12,
sm:6,
md:3,
lg:1
}}){
Text('11111111111111111111111111111111111111111111111111')
}.backgroundColor('red')
GridCol({span:{
xs:12,
sm:6,
md:3,
lg:1
}}){
Text('2')
}.backgroundColor('blue')
GridCol(){
Text('3')
}.backgroundColor('green')
GridCol(){
Text('4')
}.backgroundColor('gray')
}
.height('30%')
.width('100%')
布局的总列数
GridRow中通过columns设置栅格布局的总列数。
columns默认值为12,即在未设置columns时,任何断点下,栅格布局被分成12列。
当columns为自定义值,栅格布局在任何尺寸设备下都被分为columns列。
当columns类型为GridRowColumnOption时,支持下面六种不同尺寸(xs, sm, md, lg, xl, xxl)设备的总列数设置,各个尺寸下数值可不同。
GridRow({columns:8,direction:GridRowDirection.RowReverse,
gutter:{x:15,y:5}
}){
ForEach(this.color,(c:Color,index:number)=>{
GridCol({
span:{xs:8,sm:4,md:2,lg:1}
}){
Text(`${index}`).fontColor(Color.White)
}.backgroundColor(c)
})
}
.width('100%')
.border({style:BorderStyle.Solid,width:1,color:'red'})
排列方向
栅格布局中,可以通过设置GridRow的direction属性来指定栅格子组件在栅格容器中的排列方向。该属性可以设置为GridRowDirection.Row(从左往右排列)或GridRowDirection.RowReverse(从右往左排列)
1.GridRow({ direction: GridRowDirection.Row })子组件默认从左往右排列。
2.GridRow({ direction: GridRowDirection.RowReverse })子组件从右往左排列。
子组件间距
GridRow中通过gutter属性设置子元素在水平和垂直方向的间距。
- GridRow({ gutter: 10 })当gutter类型为number时,同时设置栅格子组件间水平和垂直方向边距且相等。下例中,设置子组件水平与垂直方向距离相邻元素的间距为10。
2.GridRow({ gutter: { x: 20, y: 50 } })当gutter类型为GutterOption时,单独设置栅格子组件水平垂直边距,x属性为水平方向间距,y为垂直方向间距。
子组件GridCol
GridCol组件作为GridRow组件的子组件,通过给GridCol传参或者设置属性两种方式,设置span(占用列数),offset(偏移列数),order(元素序号)的值。
1.GridCol({ span: 2 })设置span。
2.GridCol({ offset: 2 })设置offset。
3.GridCol({ order: 2 })设置order。
span
子组件占栅格布局的列数,决定了子组件的宽度,默认为1。
-
GridCol({ span: 2 })当类型为number时,子组件在所有尺寸设备下占用的列数相同
-
GridCol({ span: { xs: 1, sm: 2, md: 3, lg: 4 } }) 当类型为GridColColumnOption时,支持六种不同尺寸(xs, sm, md, lg, xl, xxl)设备中子组件所占列数设置,各个尺寸下数值可不同。
offset
栅格子组件相对于前一个子组件的偏移列数,默认为0。
-
GridCol({ offset: 2 })当类型为number时,子组件偏移相同列数
-
GridCol({ offset: { xs: 1, sm: 2, md: 3, lg: 4 } })当类型为GridColColumnOption时,支持六种不同尺寸(xs, sm, md, lg, xl, xxl)设备中子组件所占列数设置,各个尺寸下数值可不同。
order
栅格子组件的序号,决定子组件排列次序。当子组件不设置order或者设置相同的order, 子组件按照代码顺序展示。当子组件设置不同的order时,order较小的组件在前,较大的在后。
- GridCol({ order: 4 })当类型为number时,子组件在任何尺寸下排序次序一致。
2.GridCol({ order: { xs:1, sm:5, md:3, lg:7}}) 当类型为GridColColumnOption时,支持六种不同尺寸(xs, sm, md, lg, xl, xxl)设备中子组件排序次序设置。在xs设备中,子组件排列顺序为1234;sm为2341,md为3412,lg为2431。
嵌套使用
栅格组件也可以嵌套使用,完成一些复杂的布局。
GridRow() {
GridCol({ span: { sm: 12 } }) {
GridRow() {
GridCol({ span: { sm: 2 } }) {
Row() {
Text('left').fontSize(24)
}
.justifyContent(FlexAlign.Center)
.height('90%')
}.backgroundColor('#ff41dbaa')
GridCol({ span: { sm: 10 } }) {
Row() {
Text('right').fontSize(24)
}
.justifyContent(FlexAlign.Center)
.height('90%')
}.backgroundColor('#ff4168db')
}
.backgroundColor('#19000000')
}
GridCol({ span: { sm: 12 } }) {
Row() {
Text('footer').width('100%').textAlign(TextAlign.Center)
}.width('100%').height('10%').backgroundColor(Color.Pink)
}
}.width('100%').height(300)
媒体查询 (@ohos.mediaquery)
-
针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。
2.当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。
引入与使用流程
首先导入媒体查询模块
import { mediaquery } from '@kit.ArkUI';
通过matchMediaSync接口设置媒体查询条件,保存返回的条件监听句柄listener。例如监听横屏事件:
listener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('screen and (orientation: landscape)');//'(orientation: landscape)':横屏条件 screen and (orientation: landscape):屏幕效果
//回调函数:当屏幕发生变化时,触发的函数 //mediaquery.MediaQueryResult:媒体查询的结果
给条件监听句柄listener绑定回调函数onPortrait,当listener检测设备状态变化时执行回调函数。在回调函数内,根据不同设备状态更改页面布局或者实现业务逻辑。
onPortrait(mr:mediaquery.MediaQueryResult){//mediaquery:返回值 onPortrait:函数
if(mr.matches){//如果满足条件
this.c=Color.Red
}else {//不满足
this.c=Color.Blue
}
}
aboutToAppear(): void {//第一次调用函数
this.listener.on('change',(mr:mediaquery.MediaQueryResult)=>{//
this.onPortrait(mr)
})//调用listener on()启动监听
}
示例:
import { mediaquery } from '@kit.ArkUI';
@Entry
@Component
struct Media3Page {
@State message: string = 'Hello World';
@State isSm:boolean=true //小屏true
@State nrWidth:string='100%'//具体内容的宽度
l1:mediaquery.MediaQueryListener=this.getUIContext().getMediaQuery().matchMediaSync('(width>1080px)')//屏幕宽度 折叠屏
aboutToAppear(): void {
this.l1.on('change',(mr)=>{
if(mr.matches){//如果大于1080
this.isSm=false//变大屏
this.nrWidth='80%'//具体内容的宽度变80%
}else{//如果小于1080
this.isSm=true//变小屏
this.nrWidth='100%'//具体内容的宽度变100%
}
})
}
build() {
Column(){
if(this.isSm){//小屏显示
Row(){
Text('横向的导航')
}.width('100%').backgroundColor('#ccc').height(60)
}
Row(){
if(!this.isSm){//小屏不显示
Column(){
Text('左侧导航')
}.width('20%').backgroundColor('#ddd').height('100%')
}
Column(){
Text('具体内容')
}.backgroundColor('#eee').height('100%').width(this.nrWidth)
}.width('100%')
}
.height('100%')
.width('100%')
}
}
创建列表 (List)
列表是一种复杂的容器,当列表项达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集,例如图片和文本。
使用列表可以轻松高效地显示结构化、可滚动的信息。通过在List组件中按垂直或者水平方向线性排列子组件ListItemGroup或ListItem,为列表中的行或列提供单个视图,或使用循环渲染迭代一组行或列,或混合任意数量的单个视图和ForEach结构,构建一个列表。
布局与约束
列表作为一种容器,会自动按其滚动方向排列子组件,向列表中添加组件或从列表中移除组件会重新排列子组件。
在垂直列表中,List按垂直方向自动排列ListItemGroup或ListItem。
ListItemGroup用于列表数据的分组展示,其子组件也是ListItem。ListItem表示单个列表项,可以包含单个子组件。
布局
List除了提供垂直和水平布局能力、超出屏幕时可以滚动的自适应延伸能力之外,还提供了自适应交叉轴方向上排列个数的布局能力。
利用垂直布局能力可以构建单列或者多列垂直滚动列表
利用水平布局能力可以是构建单行或多行水平滚动列表
约束
列表的主轴方向是指子组件列的排列方向,也是列表的滚动方向。垂直于主轴的轴称为交叉轴,其方向与主轴方向相互垂直。
垂直列表的主轴是垂直方向,交叉轴是水平方向;水平列表的主轴是水平方向,交叉轴是垂直方向。
1.如果List组件主轴或交叉轴方向设置了尺寸,则其对应方向上的尺寸为设置值。
2.如果List组件主轴方向没有设置尺寸,当List子组件主轴方向总尺寸小于List的父组件尺寸时,List主轴方向尺寸自动适应子组件的总尺寸。
3.如果子组件主轴方向总尺寸超过List父组件尺寸时,List主轴方向尺寸适应List的父组件尺寸。
设置主轴方向
List组件主轴默认是垂直方向,即默认情况下不需要手动设置List方向,就可以构建一个垂直滚动列表。
若是水平滚动列表场景,将List的listDirection属性设置为Axis.Horizontal即可实现。
listDirection默认为Axis.Vertical,即主轴默认是垂直方向。
//主轴方向
@Builder test1(){
List({space:5}){
ForEach(this.nums,(n:number,index)=>{
ListItem(){
Text(n.toString())
.width(150).height(150).backgroundColor('#abc')
}
})
}
.listDirection(Axis.Horizontal)//水平方向
}
设置交叉轴布局
List组件的交叉轴布局可以通过lanes和alignListItem属性进行设置,lanes属性用于确定交叉轴排列的列表项数量,alignListItem用于设置子组件在交叉轴方向的对齐方式。
List组件的lanes属性通常用于在不同尺寸的设备自适应构建不同行数或列数的列表,即一次开发、多端部署的场景
lanes属性的取值类型是"number | LengthConstrain",即整数或者LengthConstrain类型。
//交叉轴方向
@Builder test2(){
List({space:5}){
ForEach(this.nums,(n:number,index)=>{
ListItem(){
Text(n.toString())
.width(150).height(150).backgroundColor('#abc')
}
})
}
.listDirection(Axis.Vertical)
.lanes(2)
.alignListItem(ListItemAlign.Center)
}
当其取值为LengthConstrain类型时,表示会根据LengthConstrain与List组件的尺寸自适应决定行或列数
autoList:LengthConstrain={maxLength:350,minLength:180}//最大max 最小min 主要用最小量行数
//自适应行数
@Builder test3(){
List({space:5}){
ForEach(this.nums,(n:number,index)=>{
ListItem(){
Text(n.toString())
.width(150).height(150).backgroundColor('#abc')
}
})
}
// .listDirection(Axis.Horizontal)//水平
.lanes(this.autoList)
.alignListItem(ListItemAlign.Center)
// .height(400)
.width(400)
}
由于在ListItem中只能有一个根节点组件,不支持以平铺形式使用多个组件。因此,若列表项是由多个组件元素组成的,则需要将这多个元素组合到一个容器组件内或组成一个自定义组件。
List() {
ListItem() {
Row() {
Image($r('app.media.iconE'))
.width(40)
.height(40)
.margin(10)
Text('小明')
.fontSize(20)
}
}
ListItem() {
Row() {
Image($r('app.media.iconF'))
.width(40)
.height(40)
.margin(10)
Text('小红')
.fontSize(20)
}
}
}
迭代列表内容
使用循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件,降低代码复杂度。
ArkTS通过ForEach提供了组件的循环渲染能力。
class Lxr{
touimg:ResourceStr
name:string
constructor(touimg: ResourceStr, name: string) {
this.touimg = touimg;
this.name = name;
}
}
@Entry
@Component
struct ListPage {
@State lxr:Lxr[]=[
new Lxr($r('app.media.th'),'张三'),
new Lxr($r('app.media.nai'),'王五'),
new Lxr($r('app.media.pj'),'李四'),
new Lxr($r('app.media.shoji'),'赵六'),
new Lxr($r('app.media.5'),'琪琪'),
new Lxr($r('app.media.6'),'十八'),
new Lxr($r('app.media.1'),'久久'),
new Lxr($r('app.media.7'),'试试'),
new Lxr($r('app.media.8'),'一一'),
new Lxr($r('app.media.9'),'呃呃'),
new Lxr($r('app.media.10'),'三三'),
new Lxr($r('app.media.12'),'四三'),
new Lxr($r('app.media.13'),'五三'),
]
build() {
Column(){
this.txl()
}
.height('100%')
.width('100%')
}
@Builder txl(){
List({space:10,scroller:this.sc}){
ForEach(this.lxr,(l:Lxr,index)=>{
ListItem(){
Row(){
Image(l.touimg).borderRadius(100).width(50).height(50)
Text(l.name).fontSize(30)
}.width('100%')
.backgroundColor('#aaa')
}
})
}
}
}
自定义列表样式
设置内容间距
在初始化列表时,如需在列表项之间添加间距,可以使用space参数。
List({space:10}){
//...
}
添加分隔线
分隔线用来将界面元素隔开,使单个元素更加容易识别。
List提供了divider属性用于给列表项之间添加分隔线。在设置divider属性时,可以通过strokeWidth和color属性设置分隔线的粗细和颜色。
startMargin和endMargin属性分别用于设置分隔线距离列表侧边起始端的距离和距离列表侧边结束端的距离。
class DividerTem{//分隔符
color:ResourceColor
strokeWidth:number
startMargin:number
endMargin:number
constructor(color: ResourceColor, strokeWidth: number, startMargin: number, endMargin: number) {
this.color = color;
this.strokeWidth = strokeWidth;
this.startMargin = startMargin;
this.endMargin = endMargin;
}
}
@Entry
@Component
struct ListPage {
List(){
//...
}
.divider(this.dTem)
.divider({//分隔符
color:'red',
strokeWidth:2,//宽度
startMargin:10,
endMargin:50
})
}
添加滚动条
当列表项高度(宽度)超出屏幕高度(宽度)时,列表可以沿垂直(水平)方向滚动。
在使用List组件时,可通过scrollBar属性控制列表滚动条的显示。scrollBar的取值类型为BarState,当取值为BarState.Auto表示按需显示滚动条。
scrollBar属性API version 9及以下版本默认值为BarState.Off,从API version 10版本开始默认值为BarState.Auto。
List({space:10,scroller:this.sc}){
//...
}
.scrollBar(BarState.Off)//滚动条
支持分组列表
在列表中支持数据的分组展示,可以使列表显示结构清晰,查找方便,从而提高使用效率。
在List组件中使用ListItemGroup对项目进行分组,可以构建二维列表。
在List组件中可以直接使用一个或者多个ListItemGroup组件,ListItemGroup的宽度默认充满List组件。在初始化ListItemGroup时,可通过header参数设置列表分组的头部组件。
@Builder group(str:string){
Text(str).fontSize(30).width('100%').backgroundColor('gray')
.height(50)
}
List(){
ListItemGroup({space:20,header:this.group('A')}){//header头部
ForEach(this.lxrA,(l:Lxr,i)=>{
ListItem(){
Row(){
Image(l.touimg)
.borderRadius(100).height(50).width(50)
Text(l.name).fontSize(30)
}.width('100%')
}
})
}
.divider(this.dTem1)
ListItemGroup({space:20,header:this.group('B')}){//header头部
ForEach(this.lxrB,(l:Lxr,i)=>{
ListItem(){
Row(){
Image(l.touimg)
.borderRadius(100).height(50).width(50)
Text(l.name).fontSize(30)
}.width('100%')
}
})
}
.divider(this.dTem1)
ListItemGroup({space:20,header:this.group('C')}){//header头部
ForEach(this.lxrC,(l:Lxr,i)=>{
ListItem(){
Row(){
Image(l.touimg)
.borderRadius(100).height(50).width(50)
Text(l.name).fontSize(30)
}.width('100%')
}
})
}
.divider(this.dTem1)
}
添加粘性标题
粘性标题是一种常见的标题模式,常用于定位字母列表的头部元素。
粘性标题不仅有助于阐明列表中数据的表示形式和用途,还可以帮助用户在大量信息中进行数据定位,从而避免用户在标题所在的表的顶部与感兴趣区域之间反复滚动。
List组件的sticky属性配合ListItemGroup组件使用,用于设置ListItemGroup中的头部组件是否呈现吸顶效果或者尾部组件是否呈现吸底效果。
通过给List组件设置sticky属性为StickyStyle.Header,即可实现列表的粘性标题效果。
@Builder txl3(){
List(){
ForEach(this.txls,(txl:Txl,index)=>{
ListItemGroup({header:this.group(txl.group)}){
ForEach(txl.lxr,(lxr:Lxr,i)=>{
ListItem(){
Row(){
Image(lxr.touimg)
.borderRadius(100).height(50).width(50)
Text(lxr.name).fontSize(30)
}.width('100%')
}
})
}
.divider(this.dTem1)
})
}.sticky(StickyStyle.Header)//标题不滚动
}
控制滚动位置
控制滚动位置在实际应用中十分常见,例如当新闻页列表项数量庞大,用户滚动列表到一定位置时,希望快速滚动到列表底部或返回列表顶部。
List组件初始化时,可以通过scroller参数绑定一个Scroller对象,进行列表的滚动控制。
sc:Scroller=new Scroller()//返回顶部
Stack({alignContent:Alignment.End}){
List({scroller:this.sc}){
ForEach(this.txls,(txl:Txl,index)=>{
ListItemGroup({header:this.group(txl.group)}){
ForEach(txl.lxr,(lxr:Lxr,i)=>{
ListItem(){
Row(){
Image(lxr.touimg)
.borderRadius(100).height(50).width(50)
Text(lxr.name).fontSize(30)
}.width('100%')
}
})
} .divider(this.dTem1)
})
}
.sticky(StickyStyle.Header)
}
响应滚动位置
许多应用需要监听列表的滚动位置变化并作出响应。
除了字母索引之外,滚动列表结合多级分类索引在应用开发过程中也很常见,例如购物应用的商品分类页面,多级分类也需要监听列表的滚动位置。
字母索引响应联系人列表滚动
@Builder txl4(){
Stack({alignContent:Alignment.End}){
List({scroller:this.sc}){
ForEach(this.txls,(txl:Txl,index)=>{
ListItemGroup({header:this.group(txl.group)}){
ForEach(txl.lxr,(lxr:Lxr,i)=>{
ListItem(){
Row(){
Image(lxr.touimg)
.borderRadius(100).height(50).width(50)
Text(lxr.name).fontSize(30)
}.width('100%')
}
})
} .divider(this.dTem1)
})
}
.sticky(StickyStyle.Header)
// Button('^').onClick(()=>{
// this.sc.scrollToIndex(0)
// })
.onScrollIndex((fristIndex:number)=>{
this.selected=fristIndex
})
AlphabetIndexer({arrayValue:this.xh,selected:0})
.selected(this.selected)
.selectedFont({size:40})
.font({size:30})
.itemSize(60)
}
}
通过监听List组件的onScrollIndex事件来实现,右侧索引栏需要使用字母表索引组件AlphabetIndexer。
在列表滚动时,根据列表此时所在的索引值位置firstIndex,重新计算字母索引栏对应字母的位置selectedIndex。
响应列表项侧滑
侧滑菜单在许多应用中都很常见。
ListItem的swipeAction属性可用于实现列表项的左右滑动功能。swipeAction属性方法初始化时有必填参数SwipeActionOptions,其中,start参数表示设置列表项右滑时起始端滑出的组件,end参数表示设置列表项左滑时尾端滑出的组件。
build() {
Column(){
List(){
ForEach(this.diab,(d:dianhua,i)=>{
ListItem(){
Row({space:5}){
Column({space:5}){
Text(d.names).fontSize(20).alignSelf(ItemAlign.Start)
Row({space:5}){
Text(`${d.ka}`).backgroundColor(Color.Gray).fontColor(Color.White).padding(1).fontSize(12).fontWeight(600)
Image($r('app.media.hd')).height(20)
Text(d.dizhi)
}.width('100%').justifyContent(FlexAlign.Start)
}.width('70%')
Row({space:5}){
Text(d.shijian)
Image(d.xq).width(25)
}
}.padding({left:15,right:15,top:10,bottom:10}).width('100%').justifyContent(FlexAlign.SpaceBetween)
}
.swipeAction({
end:this.shan(i)
})
})
}.height(581)
.divider({//分隔符
color:'#eee',
strokeWidth:1,//宽度
startMargin:10,
endMargin:50
})
}
.height('100%')
.width('100%')
}
@Builder shan(index:number){
Button({type:ButtonType.Normal}){
SymbolGlyph($r('sys.symbol.trash')).fontSize(30)
}.width(50).height(50).backgroundColor('#fff')
}
创建网格 (Grid/GridItem)
网格布局是由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
网格布局是由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
Grid组件为网格容器,其中容器内各条目对应一个GridItem组件
设置行列数量与占比
通过设置行列数量与尺寸占比可以确定网格布局的整体排列方式。Grid组件提供了rowsTemplate和columnsTemplate属性用于设置网格布局行列数量与尺寸占比。
rowsTemplate和columnsTemplate属性值是一个由多个空格和’数字+fr’间隔拼接的字符串,fr的个数即网格布局的行或列数,fr前面的数值大小,用于计算该行或列在网格布局宽度上的占比,最终决定该行或列宽度。
@Builder test1(){
Grid(){
ForEach([1,2,3,4,5,6,7,8,9],(n:number)=>{
GridItem(){
Text(n.toString())
}
.backgroundColor('blue')
})
}
.width('100%')
.height(300)
.rowsTemplate('1fr 1fr 1fr')//行的占比
.columnsTemplate('1fr 1fr 2fr')//列的占比
.rowsGap(5)//行间距
.columnsGap(5)//列间距
}
只要将rowsTemplate的值为’1fr 1fr 1fr’,同时将columnsTemplate的值为’1fr 2fr 1fr’,即可实现上述网格布局。
设置子组件所占行列数
通过创建Grid时传入合适的GridLayoutOptions实现如图所示的单个网格横跨多行或多列的场景
不均匀网格布局
在网格中,可以通过onGetRectByIndex返回的[rowStart,columnStart,rowSpan,columnSpan]来实现跨行跨列布局,其中rowStart和columnStart属性表示指定当前元素起始行号和起始列号,rowSpan和columnSpan属性表示指定当前元素的占用行数和占用列数。
ly:GridLayoutOptions={
regularSize:[1,1],//一个单元格所占的大小
onGetRectByIndex:(index)=>{
if(index==0){//下标是0时
//[行,列,行数,列数]
return [0,0,1,1]//0,0,坐标从第几行第几列开始 1,1,占行和列的数量
}else if(index==1){
return [0,1,1,1]
}else if(index==2){
return [0,2,1,2]
}else if(index==3){
return [1,0,2,1]
}else if(index==7){
return [1,1,1,3]
}//只需要写需要改的部分
return[0,0,1,1]//返回4个值
}
}
@Builder test2(){
Grid(undefined,this.ly){
ForEach([0,1,2,3,4,5,6,7],(n:number)=>{
GridItem(){
Text(n.toString())
}
.backgroundColor('blue')
})
}
.width('100%')
.height(300)
.rowsTemplate('1fr 1fr 1fr')//行的占比
.columnsTemplate('1fr 1fr 1fr 1fr')//列的占比
.rowsGap(5)//行间距
.columnsGap(5)//列间距
}
设置主轴方向
使用Grid构建网格布局时,若没有设置行列数量与占比,可以通过layoutDirection设置网格布局的主轴方向,决定子组件的排列方式。
当前layoutDirection设置为Row时,先从左到右排列,排满一行再排下一行。
Grid() {
...
}
.maxCount(3)
.layoutDirection(GridDirection.Row)
在网格布局中显示数据
网格布局采用二维布局的方式组织其内部元素,如下图所示
Grid组件可以通过二维布局的方式显示一组GridItem子组件。
Grid() {
GridItem() {
Text('会议')
...
}
GridItem() {
Text('签到')
...
}
GridItem() {
Text('投票')
...
}
GridItem() {
Text('打印')
...
}
}
.rowsTemplate('1fr 1fr')
.columnsTemplate('1fr 1fr')
对于内容结构相似的多个GridItem,通常更推荐使用ForEach语句中嵌套GridItem的形式,来减少重复代码。
@Builder test2(){
Grid(undefined,this.ly){
ForEach([0,1,2,3,4,5,6,7],(n:number)=>{
GridItem(){
Text(n.toString())
}
.backgroundColor('blue')
})
}
.width('100%')
.height(300)
.rowsTemplate('1fr 1fr 1fr')//行的占比
.columnsTemplate('1fr 1fr 1fr 1fr')//列的占比
.rowsGap(5)//行间距
.columnsGap(5)//列间距
}
设置行列间距
在两个网格单元之间的网格横向间距称为行间距,网格纵向间距称为列间距
通过Grid的rowsGap和columnsGap可以设置网格布局的行列间距。在图5所示的计算器中,行间距为15vp,列间距为10vp。
Grid() {
...
}
.columnsGap(10)
.rowsGap(15)
构建可滚动的网格布局
可滚动的网格布局常用在文件管理、购物或视频列表等页面中
控制滚动位置
与新闻列表的返回顶部场景类似,控制滚动位置功能在网格布局中也很常用,例如下图所示日历的翻页功能。
Grid组件初始化时,可以绑定一个Scroller对象,用于进行滚动控制,例如通过Scroller对象的scrollPage方法进行翻页。
在日历页面中,用户在点击“下一页”按钮时,应用响应点击事件,通过指定scrollPage方法的参数next为true,滚动到下一页。
private s1:Scroller=new Scroller()//滚动器
@State dates:number[]=[
25,26,27,28,29,30,31,1,
2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
1,2,3,4,5,
30,31,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
1,2,3,4,5,6,7,8,9
]
@Builder test4(){
Grid(this.s1){
ForEach(this.dates,(day:number)=>{
GridItem(){
Text(day.toString())
}
.backgroundColor('#ccc')
.height(50)
})
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
.width('100%')
.height(330)
.rowsGap(5)
.columnsGap(5)
.maxCount(3)
Row(){
Button('上一页')
.onClick(()=>{
this.s1.scrollPage({next:false})//滚动到下一页或者上一页。
})
Button('下一页')
.onClick(()=>{
this.s1.scrollPage({next:true})//滚动到下一页或者上一页。
})
}
}
创建轮播 (Swiper)
Swiper组件提供滑动轮播显示的能力。Swiper本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。
布局与约束
Swiper作为一个容器组件,如果设置了自身尺寸属性,则在轮播显示过程中均以该尺寸生效。
如果设置了prevMargin或者nextMargin属性,则Swiper自身尺寸会跟随其父组件;如果未设置prevMargin或者nextMargin属性,则会自动根据子组件的大小设置自身的尺寸。
循环播放
通过loop属性控制是否循环播放,该属性默认值为true
当loop为true时,在显示第一页或最后一页时,可以继续往前切换到前一页或者往后切换到后一页。如果loop为false,则在第一页或最后一页时,无法继续向前或者向后切换页面。
自动轮播
Swiper通过设置autoPlay属性,控制是否自动轮播子组件。该属性默认值为false。
Swiper() {
// ...
}
.loop(true)
.autoPlay(true)
.interval(1000)
导航点样式
Swiper提供了默认的导航点样式和导航点箭头样式,导航点默认显示在Swiper下方居中位置,开发者也可以通过indicator属性自定义导航点的位置和样式,导航点箭头默认不显示。
导航点使用默认样式
Swiper(this.sc){//轮播
Text('1')
.height(300).width('100%').backgroundColor('red')
Text('2')
.height(300).width('100%').backgroundColor('blue')
Text('3')
.height(300).width('100%').backgroundColor('green')
Text('4')
.height(300).width('100%').backgroundColor('gray')
Text('5')
.height(300).width('100%').backgroundColor('#faf')
}
.autoPlay(true)//自动播放
.interval(1000)//使用自动播放时播放的时间间隔
.indicator(//设置圆点
Indicator.dot()
.left(10)
.itemWidth(10)
.selectedItemWidth(20)
.itemHeight(10)
.selectedItemHeight(20)
.color('black')
.selectedColor('white')
)
自定义箭头样式
箭头显示在组件两侧,大小为18vp,导航点箭头颜色设为蓝色。
Swiper(this.sc){//轮播
Text('1')
.height(300).width('100%').backgroundColor('red')
Text('2')
.height(300).width('100%').backgroundColor('blue')
Text('3')
.height(300).width('100%').backgroundColor('green')
Text('4')
.height(300).width('100%').backgroundColor('gray')
Text('5')
.height(300).width('100%').backgroundColor('#faf')
}
.autoPlay(true)//自动播放
.interval(1000)//使用自动播放时播放的时间间隔
.indicator(//设置圆点
Indicator.dot()
.left(10)
.itemWidth(10)
.selectedItemWidth(20)
.itemHeight(10)
.selectedItemHeight(20)
.color('black')
.selectedColor('white')
)
// .displayArrow(true,false)//箭头
.displayArrow({//左右箭头
showBackground:true,//背景颜色
isSidebarMiddle:true,//位置
backgroundSize:50,//整体大小
backgroundColor:'#abcdef',//背景颜色
arrowSize:30,//箭头大小
arrowColor:Color.White//箭头颜色
},false)
页面切换方式
Swiper支持手指滑动、点击导航点和通过控制器三种方式切换页面,以下示例展示通过控制器切换页面的方法。
Swiper(this.sc){//轮播
Text('1')
.height(300).width('100%').backgroundColor('red')
Text('2')
.height(300).width('100%').backgroundColor('blue')
Text('3')
.height(300).width('100%').backgroundColor('green')
Text('4')
.height(300).width('100%').backgroundColor('gray')
Text('5')
.height(300).width('100%').backgroundColor('#faf')
}
.autoPlay(true)//自动播放
.interval(1000)//使用自动播放时播放的时间间隔
.indicator(//设置圆点
Indicator.dot()
.left(10)
.itemWidth(10)
.selectedItemWidth(20)
.itemHeight(10)
.selectedItemHeight(20)
.color('black')
.selectedColor('white')
)
// .displayArrow(true,false)//箭头
.displayArrow({//左右箭头
showBackground:true,//背景颜色
isSidebarMiddle:true,//位置
backgroundSize:50,//整体大小
backgroundColor:'#abcdef',//背景颜色
arrowSize:30,//箭头大小
arrowColor:Color.White//箭头颜色
},false)
.displayCount(2)//设置显示个数
Row(){
Button('上一页')
.onClick(()=>this.sc.showPrevious())
Button('下一页')
.onClick(()=>this.sc.showNext())
}
轮播方向
Swiper支持水平和垂直方向上进行轮播,主要通过vertical属性控制。
当vertical为true时,表示在垂直方向上进行轮播;为false时,表示在水平方向上进行轮播。vertical默认值为false。
1.设置水平方向上轮播。
Swiper() {
// ...
}
.indicator(true)
.vertical(false)
2.设置垂直方向轮播。
Swiper() {
// ...
}
.indicator(true)
.vertical(true)
每页显示多个子页面
Swiper支持在一个页面内同时显示多个子组件,通过displayCount属性设置。
Swiper(this.sc){//轮播
Text('1')
.height(300).width('100%').backgroundColor('red')
Text('2')
.height(300).width('100%').backgroundColor('blue')
Text('3')
.height(300).width('100%').backgroundColor('green')
Text('4')
.height(300).width('100%').backgroundColor('gray')
Text('5')
.height(300).width('100%').backgroundColor('#faf')
}
.autoPlay(true)//自动播放
.interval(1000)//使用自动播放时播放的时间间隔
.indicator(//设置圆点
Indicator.dot()
.left(10)
.itemWidth(10)
.selectedItemWidth(20)
.itemHeight(10)
.selectedItemHeight(20)
.color('black')
.selectedColor('white')
)
// .displayArrow(true,false)//箭头
.displayArrow({//左右箭头
showBackground:true,//背景颜色
isSidebarMiddle:true,//位置
backgroundSize:50,//整体大小
backgroundColor:'#abcdef',//背景颜色
arrowSize:30,//箭头大小
arrowColor:Color.White//箭头颜色
},false)
.displayCount(2)//设置显示个数
自定义切换动画
Swiper支持通过customContentTransition设置自定义切换动画,可以在回调中对视窗内所有页面逐帧设置透明度、缩放比例、位移、渲染层级等属性实现自定义切换动画。
Swiper(this.sc){//轮播
Text('1')
.height(300).width('100%').backgroundColor('red')
Text('2')
.height(300).width('100%').backgroundColor('blue')
Text('3')
.height(300).width('100%').backgroundColor('green')
Text('4')
.height(300).width('100%').backgroundColor('gray')
Text('5')
.height(300).width('100%').backgroundColor('#faf')
}
.autoPlay(true)//自动播放
选项卡 (Tabs)
当页面信息较多时,为了让用户能够聚焦于当前显示的内容,需要对页面内容进行分类,提高页面空间利用率。Tabs组件可以在一个页面内快速实现视图内容的切换,一方面提升查找信息的效率,另一方面精简用户单次获取到的信息量。
基本布局
Tabs组件的页面组成包含两个部分,分别是TabContent和TabBar。TabContent是内容页,TabBar是导航页签栏
Tabs使用花括号包裹TabContent
每一个TabContent对应的内容需要有一个页签,可以通过TabContent的tabBar属性进行配置。
TabContent组件上设置tabBar属性,可以设置其对应页签中的内容,tabBar作为内容的页签。
TabContent() {
Text('首页的内容').fontSize(30)
}
.tabBar('首页')
设置多个内容时,需在Tabs内按照顺序放置。
Tabs() {
TabContent() {
Text('首页的内容').fontSize(30)
}
.tabBar('首页')
TabContent() {
Text('推荐的内容').fontSize(30)
}
.tabBar('推荐')
TabContent() {
Text('发现的内容').fontSize(30)
}
.tabBar('发现')
TabContent() {
Text('我的内容').fontSize(30)
}
.tabBar("我的")
}
底部导航
底部导航是应用中最常见的一种导航方式。
导航栏位置使用Tabs的barPosition参数进行设置。默认情况下,导航栏位于顶部,此时,barPosition为BarPosition.Start。设置为底部导航时,需要将barPosition设置为BarPosition.End。
Tabs({ barPosition: BarPosition.End }) {
// TabContent的内容:首页、发现、推荐、我的
...
}
顶部导航
Tabs({ barPosition: BarPosition.Start }) {
// TabContent的内容:关注、视频、游戏、数码、科技、体育、影视
...
}
侧边导航
侧边导航是应用较为少见的一种导航模式,更多适用于横屏界面,用于对应用进行导航操作,由于用户的视觉习惯是从左到右,侧边导航栏默认为左侧侧边栏。
实现侧边导航栏需要将Tabs的vertical属性设置为true,vertical默认值为false,表明内容页和导航栏垂直方向排列。
Tabs({ barPosition: BarPosition.Start }) {
// TabContent的内容:首页、发现、推荐、我的
...
}
.vertical(true)
.barWidth(100)
.barHeight(200)
限制导航栏的滑动切换
默认情况下,导航栏都支持滑动切换,在一些内容信息量需要进行多级分类的页面
限制底部导航栏滑动
控制滑动切换的属性为scrollable,默认值为true,表示可以滑动,若要限制滑动切换页签则需要设置为false。
Tabs({ barPosition: BarPosition.End }) {
TabContent(){
Column(){
Tabs(){
// 顶部导航栏内容
...
}
}
.backgroundColor('#ff08a8f1')
.width('100%')
}
.tabBar('首页')
// 其他TabContent内容:发现、推荐、我的
...
}
.scrollable(false)
固定导航栏
当内容分类较为固定且不具有拓展性时,例如底部导航内容分类一般固定,分类数量一般在3-5个,此时使用固定导航栏
Tabs的barMode属性用于控制导航栏是否可以滚动,默认值为BarMode.Fixed。
Tabs({ barPosition: BarPosition.End }) {
// TabContent的内容:首页、发现、推荐、我的
...
}
.barMode(BarMode.Fixed)
滚动导航栏
滚动导航栏可以用于顶部导航栏或者侧边导航栏的设置,内容分类较多,屏幕宽度无法容纳所有分类页签的情况下,需要使用可滚动的导航栏,支持用户点击和滑动来加载隐藏的页签内容。
滚动导航栏需要设置Tabs组件的barMode属性,默认值为BarMode.Fixed表示为固定导航栏,BarMode.Scrollable表示可滚动导航栏。
Tabs({ barPosition: BarPosition.Start }) {
// TabContent的内容:关注、视频、游戏、数码、科技、体育、影视、人文、艺术、自然、军事
...
}
.barMode(BarMode.Scrollable)
自定义导航栏
对于底部导航栏,一般作为应用主页面功能区分,为了更好的用户体验,会组合文字以及对应语义图标表示页签内容,这种情况下,需要自定义导航页签的样式。
设置自定义导航栏需要使用tabBar的参数,以其支持的CustomBuilder的方式传入自定义的函数组件样式。
@Builder tabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
Column() {
Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
.size({ width: 25, height: 25 })
Text(title)
.fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
}
.width('100%')
.height(50)
.justifyContent(FlexAlign.Center)
}
在TabContent对应tabBar属性中传入自定义函数组件,并传递相应的参数。
TabContent() {
Column(){
Text('我的内容')
}
.width('100%')
.height('100%')
.backgroundColor('#007DFF')
}
.tabBar(this.tabBuilder('我的', 0, $r('app.media.mine_selected'), $r('app.media.mine_normal')))
切换至指定页签
此时需要使用Tabs提供的onChange事件方法,监听索引index的变化,并将当前活跃的index值传递给currentIndex,实现页签的切换。
@Builder tabBuilder(title: string, targetIndex: number) {
Column() {
Text(title)
.fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
}
}
build() {
Column() {
Tabs({ barPosition: BarPosition.End }) {
TabContent() {
...
}.tabBar(this.tabBuilder('首页', 0))
TabContent() {
...
}.tabBar(this.tabBuilder('发现', 1))
TabContent() {
...
}.tabBar(this.tabBuilder('推荐', 2))
TabContent() {
...
}.tabBar(this.tabBuilder('我的', 3))
}
.animationDuration(0)
.backgroundColor('#F1F3F5')
.onChange((index: number) => {
this.currentIndex = index
})
}.width('100%')
}
组成部分
onRequestPopupData
选中字母索引后,请求索引提示弹窗显示内容回调。
class Lxr{
tImg:ResourceStr
names:string
constructor(tImg: ResourceStr, names: string) {
this.tImg = tImg;
this.names = names;
}
}
class Txl{
key:string
lxr:Lxr[]
constructor(key: string, lxr: Lxr[]) {
this.key = key;
this.lxr = lxr;
}
}
@State strs:string[]=['A','B','C']
@State txlS:Txl[]=[
new Txl('A',[
new Lxr($r('app.media.a1'),'张三'),
new Lxr($r('app.media.a2'),'阿萨德'),
new Lxr($r('app.media.a3'),'阿是'),
new Lxr($r('app.media.a4'),'发的'),
new Lxr($r('app.media.a5'),'还不如'),
new Lxr($r('app.media.a6'),'功能'),
]),
new Txl('B',[
new Lxr($r('app.media.a1'),'各一件'),
new Lxr($r('app.media.a2'),'同意'),
new Lxr($r('app.media.a3'),'德芙'),
new Lxr($r('app.media.a4'),'他已经'),
new Lxr($r('app.media.a5'),'个人'),
new Lxr($r('app.media.a6'),'得到'),
]),
new Txl('C',[
new Lxr($r('app.media.a1'),'登革热'),
new Lxr($r('app.media.a2'),'恩格尔'),
new Lxr($r('app.media.a3'),'导入'),
new Lxr($r('app.media.a4'),'股份'),
new Lxr($r('app.media.a5'),'各位'),
new Lxr($r('app.media.a6'),'不是'),
new Lxr($r('app.media.a1'),'登革热'),
new Lxr($r('app.media.a2'),'恩格尔'),
new Lxr($r('app.media.a3'),'导入'),
new Lxr($r('app.media.a4'),'股份'),
new Lxr($r('app.media.a5'),'各位'),
new Lxr($r('app.media.a6'),'不是'),
new Lxr($r('app.media.a1'),'登革热'),
new Lxr($r('app.media.a2'),'恩格尔'),
new Lxr($r('app.media.a3'),'导入'),
new Lxr($r('app.media.a4'),'股份'),
new Lxr($r('app.media.a5'),'各位'),
new Lxr($r('app.media.a6'),'不是'),
new Lxr($r('app.media.a1'),'登革热'),
new Lxr($r('app.media.a2'),'恩格尔'),
new Lxr($r('app.media.a3'),'导入'),
new Lxr($r('app.media.a4'),'股份'),
new Lxr($r('app.media.a5'),'各位'),
new Lxr($r('app.media.a6'),'不是'),
]),
]
@Builder tou(str:string){
Text(str).width('100%').backgroundColor('gray')
}
@State selectIndex:number=0
@State strs2:string[]=[]//显示提示框的具体内容
@Builder test1(){
Stack(){
List(){
ForEach(this.txlS,(txl:Txl,index)=>{
ListItemGroup({header:this.tou(txl.key)}){
ForEach(txl.lxr,(lxr:Lxr,i)=>{
ListItem(){
Row(){
Image(lxr.tImg).height(30).width(30).borderRadius(100)
Text(lxr.names).fontSize(18)
}.width('100%')
.backgroundColor(i%2==0?'#abc':'white')
}
})
}
})
}
.sticky(StickyStyle.Header)
.onScrollIndex((first)=>{
this.selectIndex=first
})
AlphabetIndexer({arrayValue:this.strs,selected:0})
.font({size:25})
.selectedFont({size:30})
.itemSize(60)
.selected(this.selectIndex)
.usingPopup(true)//提示框
.onRequestPopupData((index:number)=>{//选中字母索引后,请求索引提示弹窗显示内容回调。
//1.清空数组
this.strs2=[]
for(let i=0;i<this.txlS[index].lxr.length;i++){
//添加数组
this.strs2.push(this.txlS[index].lxr[i].names)
}
return this.strs2
})
}
.height('100%')
.width('100%')
}
Blank
空白填充组件,在容器主轴方向上,空白填充组件具有自动填充容器空余部分的能力。仅当父组件为Row/Column/Flex时生效。
@Builder test2(){
Row(){
Text('左边')
Blank().color('red')//空白填充组件
Text('右边')
}
.backgroundColor('#abcdef')
.width('100%')
Column(){
Text('上边')
Blank().color('red')//空白填充组件
Text('下边')
}.height(100).width('100%')
.backgroundColor('#abc')
}
CalendarPicker
日历选择器组件,提供下拉日历弹窗,可以让用户选择日期。
now:Date=new Date('2023-08-09')
@State selectDate:Date=new Date('2023-08-09')
@Builder CalendarTest(){
Text('日期文本')
CalendarPicker({
hintRadius:10,//底板的圆角0~16
selected:this.now,//默认选中的日期
})
// .edgeAlign(CalendarAlign.START,{dx:100,dy:200}) //设置选择器与入口组件的对齐方式 dx:100,dy:200偏移量
.textStyle({//入口区的文本颜色、字号、字体粗细。
color:'red',
font:{size:30,weight:700}
})
.onChange((val)=>{
this.selectDate=val
})
Text('选中的日期:'+this.selectDate)
}
Checkbox
提供多选框组件,通常用于某选项的打开或关闭。
@Builder conStyle(){
SymbolGlyph($r('sys.symbol.star_fill'))
.fontColor(['red'])
}
@Builder conStyle1(num:number){
Text(num<=99?num.toString():'99+')
.fontSize(num<=99?16:10)
}
@Builder checkBoxTest(){
Row(){
Text('爱好:')
Checkbox({
name:'ah',group:'hobby'
})//提供多选框组件,通常用于某选项的打开或关闭。
.select(true)//默认选中
.selectedColor('red')//选中框的颜色
.unselectedColor('blue')//未选中的颜色
.mark({strokeColor:'#eee',size:20,strokeWidth:10})//设置多选框内部图标样式。对号样式
.shape(CheckBoxShape.ROUNDED_SQUARE)//设置CheckBox组件形状, 包括圆形和圆角方形。
Checkbox({
name:'ah',group:'hobby',indicatorBuilder:()=>{this.conStyle()}
})
Checkbox({
name:'ah',group:'hobby',indicatorBuilder:()=>{this.conStyle1(100)}
})
}
Column(){
Row(){
Text('全选')
CheckboxGroup({group:'ah'})
}
Row(){
Checkbox({group:'ah',name:'c'})
Text('唱')
Checkbox({group:'ah',name:'c'})
Text('跳')
Checkbox({group:'ah',name:'c'})
Text('rap')
}
}
}