鉴于自己对新技术的浓(ling)厚(dao)兴(yao)趣(qiu),这段时间也算正式开始学习鸿蒙开发,我们知道学习任何一个语言,都是要先从基础语法开始,而对于一个前端语言来讲,基础语法肯定是要从学习它的布局用法开始,在ArkUI里面,也有它自己的布局组件,本篇内容里面将会先了解一下ArkUI里面线性布局,层叠布局和弹性布局的用法
线性布局(Row/Column)
学过Flutter或者Compose的基本都不会对这俩个布局感到陌生,基本在哪里概念都是相同的,Row
代表着布局子元素可以在水平方向上排列布局,它的主轴就是水平方向,交叉轴就是垂直方向,Column
代表着布局子元素在垂直方向上排列,它的主轴就是垂直方向,交叉轴就是水平方向,下面就是一些常见属性在ArkTS里面的是如何设置的
间距
所谓距离产生美,在任何一个线性布局里面的元素之间,相信多多少少都会存在一些间距,那么在ArkTS里面我们使用space
属性来设置间距,来看下面这段代码
这里有个Column
组件,在组件里面添加了三个Row
组件并且使用不同的颜色区分开来,现在Row
之间是没有设置任何间距的,页面展示的样子是这样的
而通过添加space
属性就可以让Column
的子元素在主轴方向上设置间距
看到这里添加完space属性之后,Column
里面的元素之间就有间距了,当然我们也可以给子元素设置margin
,这样也可以达到同样的效果,但是space
是最直观的,Row
的间距是同样的设置方式,这里就省略了,感兴趣的可以自行尝试
对齐方式
除了间距,对齐方式也是比较常用的,对齐方式主要分为在主轴上的对齐方式和在交叉轴上的对齐方式,在主轴上我们使用justifyContent
函数来设置,入参是一个枚举类FlexAlign
,有以下几个值
下面是每个枚举值所对应的样式
FlexAlign.Start
FlexAlign.Center
FlexAlign.End
FlexAlign.SpaceBetween
FlexAlign.SpaceEvenly
FlexAlign.SpaceAround
与主轴上对齐相对应的,还有交叉轴上的对齐方式,在交叉轴上使用alignItems
函数去对齐元素,接收的参数是,如果交叉轴是水平方向的,那么使用枚举类HorizontalAlign
作为参数,所支持的枚举属性是Start
,Center
,End
,如果交叉轴方向是垂直方向的,那么使用枚举类VerticalAlign
作为参数,所支持的枚举属性是Top
,Center
,Bottom
,下面以Row
组件为例看一下交叉轴方向上如何对齐
VerticalAlign.Top
VerticalAlign.Center
VerticalAlign.Bottom
如果元素中某一个元素有自己的想法,不想用父组件定义的对齐方式,那么可以单独在子元素上设置alignSelf
,传参是一个ItemAlign
,有以下几个值
其中Baseline
,Auto
,Stretch
适用于Flex
布局,在Column
与Row
布局中只使用其他三个值Start
,Center
,End
,理解起来也很容易,分别都是在交叉轴方向上的开始位置,中间位置和结束位置,比如在上面的例子中,我们在给所有子元素设置了交叉轴方向底部对齐的基础上,给中间的元素设置顶部对齐,代码与效果图如下所示
可以看到虽然父组件设置了alignItems(VerticalAlign.Bottom)
,但是由于中间的绿色视图单独设置了alignSelf(ItemAlign.Start)
,所以它并没有底部对齐而是在父组件的顶部对齐
自适应拉伸
之前遇到过这样一个问题,在一个Row
里面有三个元素,要求是其中两个刚好是在组件左边并排着,而第三个元素是在组件的最右边,刚开始是这样实现的
可是我们现在已经知道
alignSelf
是在交叉轴方向上设置元素的对齐方式的,所以肯定是不起作用的,实际效果也如同下图所示
还有一种做法是将前两个元素包在一个组件内,然后在最外层的Row
组件上设置
justifyContent(FlexAlign.SpaceBetween)
,虽说这样可以实现,但是需要多嵌套一层布局,有没有更加简便一点的方法呢,在ArkTS里面就提供了一个叫做Blank
的组件来解决这个问题,这个组件的作用就是填充线性布局里面剩余的空间,用法也很简单,想要填充哪两个元素之间的空间,就将Blank
加在哪,就像下面这样
我们看到最后呈现的效果的确是第三个文案在最右边了
权重
我们知道组件可以通过设置with("100%")
来占满父组件宽度,height("100%")
来占满父组件高度,但是如果不去设置这俩属性,能不能也可以达到占满父组件高度或者宽度呢,我们可以通过设置权重来完成,来看下面这个例子
父组件Row
没有设置宽度,里面的元素也没有设置宽度,如果没有使用layoutWeight
来设置权重的话,屏幕上是不会出现任何元素的,但是现在因为设置了layoutWeight
,所以Row
里面的三个子元素刚好占满父组件的宽度,效果如下
让线性布局滚起来
当使用线性布局开发的时候,有一个情况不得不考虑一下,要知道如果线性布局里面的子元素在主轴方向上一屏展示不下的时候是不会换行的,那么我们如果想要看屏幕之外的元素就不得不让线性布局滚动起来,在Android里面我们会给线性布局外面套个ScrollView
,在Compose里面我们会使用scrollable
操作符,那么在ArkTS里面如何做呢,我们使用Scroll
组件,注意使用Scroll
组件的时候需要传入一个Scroller
的参数,具体用法如下所示
我们看到只需要在布局的外层套一个Scroll
组件就可以实现线性布局滚动,Scroll
还可以定义滚动条的样式以及是否常驻的属性,但不属于本篇内容就不细讲了,但是这里需要注意一点的是,当套了Scroll
组件之后,Scroll
里面的组件不能设置height
属性,不然是没法滚动的
层叠布局(Stack)
对齐方式
层叠布局Stack
可以使它的子元素可以叠在一起显示,类似于Android里面的Framelayout
,但是有区别的是FrameLayout
默认都是将子元素左上角放置,而Stack
是将子元素居中显示,我们举个栗子
这里有三个Row
组件添加在了一个Stack
组件里面,最底下的Row
宽高占了父组件宽高的80%,然后上面的占了60%,最上面的Row
占了40%,我们运行一下代码得到效果如下
可以看到三个元素都是居中显示的,如果说我们想要更改一下对齐的方式,就要使用Stack
组件提供的alignContent
参数,总共有如下几个值可供选择
比如说我们想要让子元素的对齐方式改成左上对齐,那么就可以使用Alignment.TopStart
来设置
可以看到现在的效果已经是左上角对齐了,另外我们在使用层叠布局的时候经常会遇到这种情况,就是在上面的布局经常会把下面的布局给挡住,造成的原因可能是排列顺序上出现了错误,比如我们将上面的代码更改一下
这里将原来在最下面的,宽高最大的元素放到最上面了,而将原来最上面,宽高最小的元素放在了最下面,那么得到的效果不用说肯定是下面这样的
看起来像只有一个,事实上是另外两个被蓝色的色块给遮挡住了,如果想要将三个色块都展示出来,我们可以更改一下色块的尺寸,将蓝色的色块调小,底部小的色块调大,但是我们平时开发中,布局组件大小肯定是不能随便更改的,这样做了的话设计师肯定会来找你要个说法,那么还有中办法就是更改一下Stack
里面元素的排列顺序,但是简单的布局这样改比较容易,但是复杂一点的布局改顺序就比较麻烦了,不然顺序更改错了回头再调整就会更加头大,那么还有别的办法吗?有的,我们可以使用ArkTS提供的Z序控制来解决
Z序控制
这个看名字就知道大概的意思,就是控制元素在Z轴方向上的顺序,怎么控制呢?使用zIndex
函数来实现,它里面接收一个number
类型的参数,值越大,那么显示的层级就越高,值越小,层级就越低,如果不设置zIndex
,那么层级是最低的,我们给上面的例子中添上zIndex
参数再看看效果
可以看到原本由于顺序关系在最上面的蓝色色块,现在设置了一个最小的zIndex
,就跑到最下面去了,相反原本在最下面的红色色块,由于zIndex
最大,跑到了层级的最上面,现在我们再改下代码,之前说不设置zIndex
的话层级是最小的,我们看看是不是这样
代码中我们将中间灰色色块的zIndex
给去掉了,那么我们看看原本在中间层级的灰色色块,现在还在不在
可以看到灰色色块不见了,哪去了呢?跑到蓝色色块的后面去了,所以当你想把一个布局放在所有布局的最下面,除了给它设置一个最小的zIndex
之外,还可以不给它设置,达到的效果是一样的
弹性布局(Flex)
有时候在使用Column
或者Row
的时候会遇到这样的一个情况,如果我想让元素的主轴排列顺序更改一下,就一定要改一下组件,Column
与Row
互相切换,着实有点太麻烦了,有没有一种组件能像Android的LinearLayout
一样,改一个orientation
就可以切换主轴的排列顺序呢?那么这里就可以使用弹性布局Flex
,弹性布局更像是在Row
与Column
的并集的基础上又添加了一些自己的特性,也就是说上面我们介绍的Row
与Column
的特性在Flex
上基本也都是通用的,下面就来看一下如何使用Flex
排列方向
之前就说Flex
可以切换元素的主轴方向,那么切换的方式就是通过direction
属性来切换,我们来看下面这段代码
这里在Flex
里面放了三个Text
,然后我们给Flex
的direction
属性设置了FlexDirection.Row
,那么说明我们这个Flex
就相当于一个Row
组件了,看下效果
可以看到三个Text的确是按照主轴方向为水平并且从左到右方向上排列,为什么这里要强调说明一下是从左到右呢,因为Flex还可以设置子元素在水平方向上从右到左排列,使用FlexDirection.RowReverse
实现,我们更改一下上面这段代码
看到这里已经将Flex
的direction
更改为FlexDirection.RowReverse
,那么得到的效果就是下面这样
看到元素的排列方向已经更改为从右到左的了,这个是Row
组件无法实现的,在Flex
上做了一些扩展,同样的也可以在垂直方向上设置从上到下排列和从下到上排列
换行
之前在介绍Row
组件的时候说起过一点,如果Row
组件里面的元素在主轴方向上排列超过一屏了,那么超出部分将会看不到的,如果想要看到超出部分的元素,那么就必须让Row
组件套在一个Scroll
里面让它可以滚动,但是这个问题在Flex
里面就不存在了,因为Flex
允许它的子元素可以换行,通过设置wrap
属性,默认是FlexWrap.NoWrap
的,表示不换行,但是如果子元素在主轴上超出了一个屏幕,那么子元素会等比例缩放直到可以铺满一个屏幕,我们看下面这段代码
这里有个Column
组件,里面有一个Flex
跟Row
,分别都水平方向上展示10个Text
组件,但是每个Text
的宽度都设置为80,那么明显所有Text
的总宽度是会超出一个屏幕的,我们看下实际效果
可以看到在Row
里面超出部分是看不到的,而在Flex
里面,所有元素通过等比例缩放全部都展示在一个屏幕里面,这就是Flex
在线性布局上属于自己的特性,而除了FlexWrap.NoWrap
之外,我们还可以设置FlexWrap.Wrap
允许Flex
在元素超出一行的时候换行,我们更改一下上面的代码
现在Flex
设置了FlexWrap.Wrap
,那么里面的元素在超出一行的时候,就不会等比例缩放了,而是会换行显示,效果如下
FlexWrap
还有另一个值,那就是FlexWrap.WrapReverse
,看名字就知道干什么用的,Flex
正常排序都是从左到右的,而FlexWrap.WrapReverse
可以让元素从右到左排序,上面的例子换成FlexWrap.WrapReverse
后的效果如下
主轴对齐
Flex
主轴上的对齐方式与上面的Row
/Column
基本相同,唯一的差别是Flex
是通过一个属性设置的,而Row
/Column
是通过一个函数设置的
可以看到无论是属性名字还是属性值,都与之前讲的
Row
与Column
主轴上的对齐方式相同,所以由于上面已经讲过这类对齐方式的用法了,这里就不再重复讲了
交叉轴对齐
Flex
在交叉轴上的对齐方式分为在容器上设置与给子元素设置,在容器上我们同样使用属性alignItems
来设置,属性值同样有以下几种
其中ItemAlign.Start
,ItemAlign.Center
,ItemAlign.End
在讲Row与Column时候已经说过了,这里讲一下其他三个ItemAlign.Auto
,ItemAlign.Baseline
,ItemAlign.Stretch
ItemAlign.Auto
这是Flex默认设置,不管是官方api文档或者是源码注释都是只有这一个说法,那到底ItemAlign.Auto
是怎么样的一个呈现样式呢,我们写一个例子看看
这里同样在一个Flex
组件里面放了三个Text
,Text
上没有设置任何对齐方式,Flex
给子组件在交叉轴上设置了一个ItemAlign.Auto
的对齐方式,当然由于是默认设置,这里写不写ItemAlign.Auto
都是一样的,我们看下效果
所以ItemAlign.Auto
表示的意思就是子组件在交叉轴上顶部对齐,同ItemAlign.Start
的效果是一致的
ItemAlign.Baseline
再来看下ItemAlign.Baseline
,这个表示基于文本的baseline对齐,我们将上述例子中交叉轴的对齐方式改成ItemAlign.Baseline
试试看
这里看起来好像跟ItemAlign.Center
没啥区别,我们加点代码让效果明显一些
这里将每个Text的宽度加大一些,然后字体大小也设置成不一样,再来看看效果
现在效果就明显多了,ItemAlign.Baseline
就是让元素在交叉轴方向上基于文本底部对齐
ItemAlign.Strech
再来看下ItemAlign.Strech
,这个表示在交叉轴方向上拉伸填充,上面的代码再稍作修改
这里将交叉轴方向的对齐方式改成ItemAlign.Strech
了,并且将第一个第三个元素的高度设置去掉,最终的效果是这样的
可以看到子组件不管是有没有设置高度,只要容器设置了ItemAlign.Strech
,那么子组件在交叉轴方向上都是填充整个容器的
跟Row
或者Column
相似,除了可以在容器上设置子组件在交叉轴上的对齐方式之外,还可以单独在某个子组件上设置交叉轴上的对齐方式,同样也是用alignSelf
函数,子组件的设置可以覆盖容器上的设置,下面是一个具体例子
我们在上面的例子中又新增了两个Text组件,容器上设置的交叉轴的对齐方式是居中,然后子组件里面除了最后一个,其他子组件都设置了与容器不一样的对齐方式,实际效果如下
可以看到只有第五个组件是按照容器的设置来对齐,其余的都是按照各自的设置来对齐
内容对齐
由于Flex
组件支持子组件换行展示,所以在交叉轴方向上子组件数量也有可能超出一个,所以针对Flex
的这个特性,在交叉轴方向也增加了同主轴一样的对齐方式,我们使用alignContent
属性去设置,取值与主轴一样
下面是这六个取值与它们各自的呈现效果
FlexAlign.Start
FlexAlign.Center
FlexAlign.End
FlexAlign.SpaceBetween
FlexAlign.SpaceAround
FlexAlign.SpaceEvenly
注意这里的alignContent
属性只有当wrap
为FlexWrap.Wrap
或者FlexWrap.WrapReverse
才有效
自适应拉伸
这个也能体现出Flex
“弹性”的特征,Flex
里面的子组件可以使用几个属性来更改自身在主轴上的大小来适应父组件的大小,下面逐个介绍下
flexBasis
当子组件设置了这个属性,并且值为auto
的时候,子组件的大小以自身的宽高为准,当flexBasis
属性设置了某一个具体的值的时候子组件主轴上的大小以flexBasis
设置的值为准,来看下面这段代码
这个例子里,在Flex
里面也放了三个Text
,Flex
的主轴方向为水平方向,其中第一个Text
设置了宽度为50,flexBasis
设置为auto
,第二个Text
宽度为50,flexBasis
设置成100,第三个Text
没有设置宽度,flexBasis
设置成80,我们来看下这三个Text
最终呈现的效果如何
可以看到第一个Text
由于flexBasis
设置了auto
,所以宽度还是按照设置的width
的大小,第二个Text
由于flexBasis
设置了成100,所以最终宽度以flexBasis
为准也是100,第三个Text
虽然没有设置width
,但由于flexBasis
设置了成80,所以最终也是有80的宽度
flexGrow
当子组件设置了这个属性的时候,那就意味着要瓜分父组件里面剩余的空间了,瓜分的规则就是按照每个子组件设置的flexGrow
的值来按比例瓜分
我们更改了一下上面那段代码,让三个色块的宽度都为50,并且第一个色块设置了
flexGrow
为1,第二个色块设置了flexGrow
为4,我们看到由于它们所在的父组件宽度为300,所以第一,二个色块会瓜分剩余的150的空间,第三个色块由于没有设置flexGrow
,将不参与瓜分,那么如何瓜分呢,就是按照各自设置的比例来,第一个色块除了自身宽度,还会加上150 * 1/5的宽度,第二个色块会加上150 * 4/5的宽度,为了验证我说的对不对,我们在上面这段代码中再加点代码
我们在这三个色块的下面同时再放上一个宽度为80的色块和一个宽度为170的色块,看看最终如果上下两边的前两个色块大小如果一样的话,那就说明我说的瓜分规则是对的,来看下效果
说明没有问题,我们再来看下一个属性
flexShrink
这个刚好与上面那个flexGrow
的作用相反,flexGrow
是当父组件有剩余空间时候,瓜分剩余空间,而flexShrink
是当父组件空间不足的时候,将子组件按照比例压缩来适配父组件,同样来看下下面这段代码
在这段代码里面,我们有两个
Flex
组件分别套在一个Column
里面,上面那个Flex
宽度为300,里面有三个宽度分别为150的Text
,由于这三个Text的宽度之和是大于父组件的宽度的,所以将前两个设置了flexShrink
,让它们可以按照设置的比例压缩一下它们的宽度,第三个则没有设置,在第二个Flex
里面,宽度也设置为300,里面放了两个Text
,并且宽度都设置为150,这样做的目的是让第一个Flex
里面的Text
经过压缩后与第二个Flex
里面的Text
做下比较,看看最终的压缩效果如何,运行代码之后效果图如下
这里开始让我感到困惑的地方就来了,可能是刚看完flexGrow
的原因,让我惯性思维的觉得只有设置了flexShrink
的组件才会压缩自己,但我们明显可以从效果图中发现,在上面Flex
中没有设置flexShrink
的第三个Text
,它的宽度也已经不再是150了,明显要比下面Flex
中第二个Text
要短,显然也是压缩了一定的长度,那么究竟第三个Text
压缩了多少呢?前两个设置了flexShrink
的意义又在哪里呢?当我觉得这有可能是ArkTS里面的一个bug的时候,忽然想到了Flex
不是默认就可以让子组件在主轴方向上压缩尺寸吗?这个特性与我们现在讲的这个flexShrink
是不是有某种关联呢?有没有可能Flex
的子组件就默认自带了flexShrink
属性,为了验证这一点,我打算做个实验,来看下面这段代码
依然还是上下两个宽度为300的Flex
,里面子组件也都是宽度150的Text
,唯一区别在于上面的Text
都设置了flexShrink(1)
,下面的Text
没有设置flexShrink
,我们运行下看看上下Text
压缩后的实际效果
可以看到完全一样,所以可以得出结论,Flex
的子组件当Flex
不能换行的时候,默认自带flexShrink(1)
的属性,那么又引出来一个问题,Flex
的子组件是如何通过flexShrink
设置的值来计算实际压缩的大小呢?这个比较简单,我们先来计算下实际三个Text
的宽度之和与父组件的宽度差,得到150的值,也就是总共压缩了150的宽度,然后除以所有组件设置的flexShrink
之和,得到单位压缩长度50,由于这里每个Text
都是设置的flexShrink(1)
,所以每个Text
都减小了50的宽度,最终每个Text
的宽度都变成了100,我们讲上述代码中第二个Flex
中的Text
的宽度都固定为100,与第一个Flex
中的Text
做个比较,看看我们的结论是否正确
完全一样,说明第一个Flex
里面的Text
最终压缩后的宽度就是100,现在我们再回到第一个例子中,第一个Text
压缩比例是2,第二个Text
压缩比例是3,第三个Text
压缩比例是1,那么实际各自减去的宽度为2 * 150/6 = 50,3 * 150/6 = 75,1 * 150/6 = 25,我们在这个Flex的底下放上一个Row
,里面添加三个宽度分别为100,75,125的Text
组件对比下是否一样
好了,flexShrink
如何压缩子组件的,现在也算是给整明白了。
总结
三个布局组件讲完了,篇幅还是蛮大的,其实看过鸿蒙开发者官方文档的人应该也都发现了,本篇文章知识点的讲解顺序就是按照官方文档来的,看了每个知识点,然后分别手写下对应的demo和自己的理解,后面其他布局组件都也会以这种方式讲解,枯燥是枯燥了点,但相比于仅仅是看文档来说,这才是最有效的掌握知识点的方法。