一、前言
动态化使用 jue 语言(开发风格与 Vue 一致)开发,对于视图的布局采用了标准的Flex布局方式。对于列表类视图,动态化提供了<scroll>、<slider>、<recycle-list>、<waterfall>等标签,将子视图的布局管理封装到标签中实现,业务只需要针对标签简单地设置相关属性,即可实现列表类布局,大幅提升研发效率。同时动态化也支持绝对布局以及控制视图的显示和隐藏等功能,使之能胜任绝大多数业务布局场景。
在京东金融App使用动态化方案适配鸿蒙系统的过程中发现,鸿蒙提供的Flex布局和W3C标准的Flex布局,在一些常用的默认属性上表现不一致。导致原本在Android、iOS和web端显示正确的UI布局在鸿蒙端显示错乱。经与鸿蒙的工程师沟通,华为出于多方面的考虑,并没有调整的计划。故而决定废弃鸿蒙提供的布局方式,使用标准的 Flex 布局,引入yoga布局库(由 FaceBook 提供一个开源的跨平台的布局引擎),yoga库的功能是解析视图的 CSS 设置,获取视图的位置(x,y)和尺寸(width,height),直接设置给视图,确保视图的正确显示。
下面详细介绍一下鸿蒙业务在实际开发中高频使用的布局方式,这些布局方式可以涵盖95%以上的业务布局场景。
二、Flex布局
1. 概念
2. 容器(container)的常用属性
2.1 flex-direction
flex-direction: row | row-reverse | column | column-reverse;
以下是假设主轴是row的情况下,介绍其他属性
2.2 flex-wrap
items在主轴上一行显示不下的情况下的折行方式,默认值是nowrap。
flex-wrap: nowrap | wrap | wrap-reverse;
不同 flex-wrap 的显示效果:
2.3 flex-flow
这是flex-direction和flex-wrap的简写方式,默认值是 column nowrap 。
flex-flow:column nowrap;
2.4 justify-content
定义item在主轴上的对齐方式,默认值是 flex-start 。
justify-content: flex-start | center | space-between | space-around | space-evenly | flex-end;
不同 justify-content 的显示效果:
2.5 align-items
align-items: flex-start | center | flex-end | stretch;
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
<div style="height: 200px;flex-direction: row; justify-content: flex-start; align-items: flex-start;flex-wrap: wrap;align-content: stretch;"><text class="item" style="align-self: flex-end;">1</text><text class="item">2</text><text class="item">3</text><text class="item">4</text><text class="item">5</text><text class="item">6</text><text class="item" style="align-self: flex-end;">7</text></div>
3.项目(item)常用属性
3.1 flex-grow
<div style="flex-direction: row;height: 60px;width: 300px; background-color: blue;"><div style="width:100px;flex-grow:1; height: 40px;background-color: red;"></div><div style="width:50px;flex-grow:2; height: 40px;background-color: green;"></div></div>
3.2 flex-shrink
item在container中的主轴方向上的缩小系数,默认值为0,即空间不够,也不缩小。这个属性仅在container无法承载item的情况下才生效。举个例子:
<div style="flex-direction: row;height: 60px;width: 300px;background-color: blue;"><div style="width:300px;flex-shrink:1; height:40px;background-color: red;"></div><div style="width:300px;flex-shrink:2; height:40px;background-color: green;"></div></div>
由于item1与item2的宽度一致,item1压缩的比例为item1的2倍,因此最终item1的尺寸是item的2倍。UI效果如下:
3.3 flex-basis
container在分配多余空间之前,item在主轴方向上占据的空间,默认值是auto,即本身默认的大小。举个例子:
<div style="flex-direction: row;height: 60px;width: 300px;background-color: blue;padding-top: 10px;"><div style="width:150px;flex-grow:1; height:40px;background-color: red;"></div><div style="width:150px;flex-grow:1; flex-basis: 50px; height:40px;background-color: green;"></div></div>
由于item1宽度为150px;flex-basis默认为auto,因此默认为150px;item2宽度为150px,但flex-basis为50px,因此在计算所占空间时按50px算,这样在container的主轴上的剩余空间为 300 - 150 - 50 = 100px;item1和item2的flex-grow都为1,因此,各站剩余空间的1/2。因此item1的宽为150+50=200px;item2为50+50=100px;UI效果如下:
flex: none | [ <'flex-grow'> <'flex-shrink'> <'flex-basis'> ]
flex:1 等价于 flex: 1 1 auto; flex:none 等价于 flex : 0 0 auto;
注意:在使用的时候经常习惯性的使用flex:1;很多时候大家想要的仅仅是 flex-grow:1;如非必要,尽量不要写无用约束。
3.4 align-self
align-self: auto | flex-start | flex-end | center | stretch;
三、列表类布局
对于列表布局,动态化提供了<scroll>、<slider>、<recycle-list>、<waterfall>、<lego-container>、<feed-container>等标签,对于多页面管理提供<page-container>、<tab-bar>等标签。业务仅通过修改标签属性,即可实现子视图在列表内以不同的布局方式显示。
以下是列表在京东金融中的几个具体使用场景。
四、绝对布局
1.position: absolute;
.item {position: absolute;right:10px;bottom:20px;width:50px;height:50px;}
五、视图的显示和隐藏控制
1. v-if = true | false
<div v-if="false" class="item"></div>
2. display : flex | none;
3. v-show=true | false ;
4. visibility : visible | hidden;
5. opacity : 0 | 1
6. overflow: hidden | visible
六、布局问题分析
1. 为什么没有给item设置宽,item也没有子视图,但item却有宽呢?
因为在动态化上视图的主轴默认是column,align-items默认是stretch,因此item会填充父容器。
2. 为什么给item设置了宽,item显示的宽却是别的值?
这个情况就要具体分析了,如果你在设置了宽的情况下,还设置了flex:1;或者flex-grow:1;再或者flex-shrink等,item可能会被拉伸或者压缩,导致宽度变化。也有可能其他item的缩放级别更高,优先满足其他item的显示,当前item的尺寸也有可能被压缩。
3.item既可以从container的设置中获取尺寸,也可以被子item撑开获取尺寸,也可以自己设置尺寸,还可以被拉伸或者压缩。那item的尺寸最终是由谁来决定呢?
首先假设主轴为 column。container的align-items默认是stretch,因此item会填充父容器。但是如果item自身设置了宽,则会覆盖stretch。如果item的align-self不是stretch,本身也没有设置宽,但子视图有宽,可以被子视图撑开。如果自身设置了宽,则不受子视图影响。
在主轴方向上:如果item本身没有设置高,但子视图有高,则可以子视图撑开。如果自身设置了高,就不受子视图的影响。如果自身设置了flex-grow:1则会填充父视图,同样如果设置了 flex-shrink:1,空间不足则被压缩,都会覆盖自身设置的高。
最后:max-width 和 max-height 优先级最高,如果触发边界条件,就会生效,从而覆盖其他设置。
4. 如何让n个item按照比例占据container的主轴空间?
5. 对于一个复杂的视图,我该如何下手?
其实越是复杂的视图实现的方式就越多,没有固定的、标准的实现方式。下面说一下我的一点看法,我将视图分为以下几种场景:
七、写在最后

扫一扫,加入技术交流群

