作者:京東科技 劉寧
一、前言
動(dòng)態(tài)化使用 jue 語言(開發(fā)風(fēng)格與 Vue 一致)開發(fā),對(duì)于視圖的布局采用了標(biāo)準(zhǔn)的Flex 布局方式。對(duì)于列表類視圖,動(dòng)態(tài)化提供了、、、等標(biāo)簽,將子視圖的布局管理封裝到標(biāo)簽中實(shí)現(xiàn),業(yè)務(wù)只需要針對(duì)標(biāo)簽簡單地設(shè)置相關(guān)屬性,即可實(shí)現(xiàn)列表類布局,大幅提升研發(fā)效率。同時(shí)動(dòng)態(tài)化也支持絕對(duì)布局以及控制視圖的顯示和隱藏等功能,使之能勝任絕大多數(shù)業(yè)務(wù)布局場(chǎng)景。
在京東金融App使用動(dòng)態(tài)化方案適配鴻蒙系統(tǒng)的過程中發(fā)現(xiàn),鴻蒙提供的Flex布局和W3C標(biāo)準(zhǔn)的Flex布局,在一些常用的默認(rèn)屬性上表現(xiàn)不一致。導(dǎo)致原本在Android、iOS和web端顯示正確的UI布局在鴻蒙端顯示錯(cuò)亂。經(jīng)與鴻蒙的工程師溝通,華為出于多方面的考慮,并沒有調(diào)整的計(jì)劃。故而決定廢棄鴻蒙提供的布局方式,使用標(biāo)準(zhǔn)的 Flex 布局,引入yoga布局庫(由 FaceBook 提供一個(gè)開源的跨平臺(tái)的布局引擎),yoga庫的功能是解析視圖的 CSS 設(shè)置,獲取視圖的位置(x,y)和尺寸(width,height),直接設(shè)置給視圖,確保視圖的正確顯示。
下面詳細(xì)介紹一下鴻蒙業(yè)務(wù)在實(shí)際開發(fā)中高頻使用的布局方式,這些布局方式可以涵蓋95%以上的業(yè)務(wù)布局場(chǎng)景。
二、Flex布局
在動(dòng)態(tài)化上,視圖默認(rèn)使用的就是flex布局方式(相當(dāng)于默認(rèn)設(shè)置了 display:flex;)。
1. 概念
容器(container):是指開啟了flex布局的視圖
項(xiàng)目(item):是指容器的子視圖
容器(container)有幾個(gè)重要的概念
主軸:item在container上的排列方向,主軸開始的位置叫 main start, 主軸結(jié)束的位置叫 main end;item在主軸上占據(jù)的尺寸叫 main size
交叉軸:與主軸垂直的方向;交叉軸開始的位置叫 cross start ,交叉軸結(jié)束的位置叫 cross end;item 在交叉軸上占據(jù)的尺寸叫 cross size
2. 容器(container)的常用屬性
2.1 flex-direction
設(shè)置主軸方向,默認(rèn)值是column(以下所說的默認(rèn)都是指在動(dòng)態(tài)化上的默認(rèn)設(shè)置)。大部分設(shè)置看效果圖已經(jīng)很清晰,故不做過多解釋。
flex-direction: row | row-reverse | column | column-reverse;
不同主軸的顯示效果:
以下是假設(shè)主軸是row的情況下,介紹其他屬性
2.2 flex-wrap
items在主軸上一行顯示不下的情況下的折行方式,默認(rèn)值是nowrap。
flex-wrap: nowrap | wrap | wrap-reverse;
不同 flex-wrap 的顯示效果:
2.3 flex-flow
這是flex-direction和flex-wrap的簡寫方式,默認(rèn)值是 column nowrap 。
flex-flow:column nowrap;
2.4 justify-content
定義item在主軸上的對(duì)齊方式,默認(rèn)值是 flex-start 。
justify-content: flex-start | center | space-between | space-around | space-evenly | flex-end;
不同 justify-content 的顯示效果:
2.5 align-items
定義item在交叉軸上的對(duì)齊方式,默認(rèn)是stretch。
align-items: flex-start | center | flex-end | stretch;
不同 align-items 的顯示效果:
2.6 align-content
定義多根主軸在在交叉軸上的對(duì)齊方式(單條主軸情況下這個(gè)屬性不生效)。
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
這個(gè)剛接觸的同學(xué)不好理解,什么是主軸的對(duì)齊方法?拿 stretch 舉個(gè)例子
1/text?> 2/text?> 3/text?> 4/text?> 5/text?> 6/text?> 7/text?>
UI顯示效果如下:
藍(lán)色為 container,主軸為 row(flex-direction: row;),支持折行(flex-wrap: wrap;),多行主軸的對(duì)齊方式為 stretch(align-content: stretch;),此例中有兩條主軸,因此兩條主軸的高將充滿 container 的高;
第一條主軸中包含視圖 1,2,3,4,5。第二條主軸包含視圖 6,7。單條主軸的在豎直方向上的對(duì)齊方式為從上到下(align-items: flex-start;),而 1 改變了所在主軸的交叉軸方向的對(duì)齊方式為從下到上(align-self: flex-end),因此1底部對(duì)齊,2,3,4,5頂部對(duì)齊。 6,7 同理。
3.項(xiàng)目(item)常用屬性
3.1 flex-grow
item在container中的主軸方向上的剩余空間中占據(jù)的分配比例系數(shù)(默認(rèn)值為0,即不放大)。初次聽起來不理解,看個(gè)例子就清楚了:
UI效果如下:
由于 item1 寬度為100px,item2 寬度為 50px, container在主軸剩余空間為300-100-50 = 150px;根據(jù)flex-grow 的設(shè)置,item1占1/3,為50px;item2占2/3,為100px;故最終 item1 與 item2 平分了主軸空間。
3.2 flex-shrink
item在container中的主軸方向上的縮小系數(shù),默認(rèn)值為0,即空間不夠,也不縮小。這個(gè)屬性僅在container無法承載item的情況下才生效。舉個(gè)例子:
由于item1與item2的寬度一致,item1壓縮的比例為item1的2倍,因此最終item1的尺寸是item的2倍。UI效果如下:
3.3 flex-basis
container在分配多余空間之前,item在主軸方向上占據(jù)的空間,默認(rèn)值是auto,即本身默認(rèn)的大小。舉個(gè)例子:
由于item1寬度為150px;flex-basis默認(rèn)為auto,因此默認(rèn)為150px; item2寬度為150px,但flex-basis為50px,因此在計(jì)算所占空間時(shí)按50px算,這樣在container的主軸上的剩余空間為 300 - 150 - 50 = 100px;item1和item2的flex-grow都為1,因此,各站剩余空間的1/2。因此item1的寬為150+50=200px;item2為50+50=100px;UI效果如下:
3.4 flex
這是一個(gè)復(fù)合屬性,是flex-grow、flex-shrink 、flex-basis的簡寫形式。
flex: none | [ 'flex-grow'?> 'flex-shrink'?> 'flex-basis'?> ]
flex:1 等價(jià)于 flex: 1 1 auto; flex:none 等價(jià)于 flex : 0 0 auto;
注意:在使用的時(shí)候經(jīng)常習(xí)慣性的使用 flex:1;很多時(shí)候大家想要的僅僅是 flex-grow:1;如非必要,盡量不要寫無用約束。
3.4 align-self
item在交叉軸上的布局方式由container的align-items屬性設(shè)置,如果某個(gè)item想使用別的對(duì)齊方式,可以給item設(shè)置align-self,就會(huì)覆蓋container上的設(shè)置。默認(rèn)值為auto,表示按照container的align-items樣式,如果沒有父元素,則用stretch。
align-self: auto | flex-start | flex-end | center | stretch;
三、列表類布局
對(duì)于列表布局,動(dòng)態(tài)化提供了、、、、、等標(biāo)簽,對(duì)于多頁面管理提供、等標(biāo)簽。業(yè)務(wù)僅通過修改標(biāo)簽屬性,即可實(shí)現(xiàn)子視圖在列表內(nèi)以不同的布局方式顯示。
以下是列表在京東金融中的幾個(gè)具體使用場(chǎng)景。
四、絕對(duì)布局
對(duì)于要脫離視圖文檔流,不受兄弟視圖布局影響,固定顯示于某個(gè)指定位置的子視圖,可用絕對(duì)布局,比如懸浮圖標(biāo)等。
1.position: absolute;
.item { position: absolute; right:10px; bottom:20px; width:50px; height:50px; }
絕對(duì)定位是相對(duì)于父視圖的,使用left、right、top、bottom、width和height來設(shè)置。
五、視圖的顯示和隱藏控制
在開發(fā)過沖中,經(jīng)常會(huì)用讓視圖顯示或者隱藏的需求。在動(dòng)態(tài)化上提供如下幾種方式來實(shí)現(xiàn),可根據(jù)具體的使用場(chǎng)景選擇使用哪一種。
1. v-if = true | false
這種隱藏方式最徹底,會(huì)銷毀視圖,不占據(jù)任何空間,后面如果再需要顯示,則元素會(huì)重新創(chuàng)建。這種顯示/隱藏元素比較徹底,會(huì)帶來的內(nèi)存的開辟和回收,一般用在只顯示某一種具體元素的場(chǎng)景下使用。
2. display : flex | none;
這種隱藏方式,不會(huì)銷毀元素,仍存在于HTML文檔中,仍可通過JavaScript訪問和操作,但元素會(huì)從布局上移除,元素不占據(jù)任何空間,不能參與用戶交互事件。
3. v-show=true | false ;
在動(dòng)態(tài)化上會(huì)被解析為display:flex | none;
4. visibility : visible | hidden;
這種隱藏方式,只是從視覺上移除,仍然參與頁面布局,保留元素的位置,無法參與用戶交互。
5. opacity : 0 | 1
這種方式是設(shè)置視圖的透明度,在透明度為0時(shí),完全隱藏,視圖仍參與頁面布局,保留元素位置,在 Web、Android 和Harmony端仍可參與用戶交互,但在 iOS 端只有當(dāng)透明度大于0.1時(shí)可以。這個(gè)一般用作視圖顯示或者消失的過度動(dòng)畫。
6. overflow: hidden | visible
規(guī)定視圖超出父視圖邊界時(shí)如何處理。在Harmony/iOS/Web 端默認(rèn)值為visible,即超出可見,Android端默認(rèn)值為hidden,超出不可見。如果設(shè)置了 visible ,如果視圖本身添加了事件,對(duì)于超出父視圖的部分,Harmony/Android/Web仍可響應(yīng)事件,在 iOS 端無法響應(yīng)事件。
多說一句:動(dòng)態(tài)化作為一個(gè)跨端框架,會(huì)最大限度的抹平各端差異,但在有些系統(tǒng)特性上,還是會(huì)保持各端的默認(rèn)實(shí)現(xiàn)。業(yè)務(wù)在使用時(shí),可根據(jù)具體情況,具體處理。
六、布局問題分析
動(dòng)態(tài)化在京東金融 App 里已經(jīng)大范圍使用,截止到 2024 年 8 月 26 日已有200+ 頁面,200+卡片使用動(dòng)態(tài)化技術(shù)開發(fā)。在這個(gè)過程中與各業(yè)務(wù)一起解決了很多布局相關(guān)的問題。選擇幾個(gè)有共性的問題分享給大家。
1. 為什么沒有給item設(shè)置寬,item也沒有子視圖,但item卻有寬呢?
因?yàn)樵趧?dòng)態(tài)化上視圖的主軸默認(rèn)是column,align-items默認(rèn)是stretch,因此item會(huì)填充父容器。
2. 為什么給item設(shè)置了寬,item顯示的寬卻是別的值?
這個(gè)情況就要具體分析了,如果你在設(shè)置了寬的情況下,還設(shè)置了flex:1;或者flex-grow:1;再或者flex-shrink等,item可能會(huì)被拉伸或者壓縮,導(dǎo)致寬度變化。也有可能其他item的縮放級(jí)別更高,優(yōu)先滿足其他item的顯示,當(dāng)前item的尺寸也有可能被壓縮。
3. item既可以從container的設(shè)置中獲取尺寸,也可以被子item撐開獲取尺寸,也可以自己設(shè)置尺寸,還可以被拉伸或者壓縮。那item的尺寸最終是由誰來決定呢?
首先假設(shè)主軸為 column。container的align-items默認(rèn)是stretch,因此item會(huì)填充父容器。但是如果item自身設(shè)置了寬,則會(huì)覆蓋stretch。如果item的align-self不是stretch,本身也沒有設(shè)置寬,但子視圖有寬,可以被子視圖撐開。如果自身設(shè)置了寬,則不受子視圖影響。
在主軸方向上:如果item 本身沒有設(shè)置高,但子視圖有高,則可以子視圖撐開。如果自身設(shè)置了高,就不受子視圖的影響。如果自身設(shè)置了 flex-grow:1則會(huì)填充父視圖,同樣如果設(shè)置了 flex-shrink:1,空間不足則被壓縮,都會(huì)覆蓋自身設(shè)置的高。
最后:max-width 和 max-height 優(yōu)先級(jí)最高,如果觸發(fā)邊界條件,就會(huì)生效,從而覆蓋其他設(shè)置。
4. 如何讓n個(gè)item按照比例占據(jù)container的主軸空間?
對(duì)于這類比例問題,可設(shè)置所有item 的flex-basis 為 0,把自身空間全部收回,通過 flex-grow 分配對(duì)應(yīng)比例即可。
5. 對(duì)于一個(gè)復(fù)雜的視圖,我該如何下手?
其實(shí)越是復(fù)雜的視圖實(shí)現(xiàn)的方式就越多,沒有固定的、標(biāo)準(zhǔn)的實(shí)現(xiàn)方式。下面說一下我的一點(diǎn)看法,我將視圖分為以下幾種場(chǎng)景:
1.如果視圖自身知道具體的寬高,那就直接設(shè)置,這是效率最高的方式。
2.如果視圖自身不知道寬高,但是父視圖有固定的尺寸,想辦法從父視圖獲取。
3.如果視圖自身不知道寬高,但子視圖有寬高,則可以靠子視圖撐開。
4.如果必要,使用 max-width 和 max-height 做最高層級(jí)的條件約束。
其他的情況一般都是以上四種情況的組合。只要理清視圖間約束條件,排除約束矛盾點(diǎn),確定優(yōu)先級(jí)。先建立清晰的布局思路,明確視圖的尺寸來源,按照既定思路布局,不寫無效約束,一切就清晰自然。
七、寫在最后
動(dòng)態(tài)化是一個(gè)涉及JavaScript、iOS、Android、Harmony、Java、Vue、Node、Webpack等眾多領(lǐng)域的綜合解決方案,我們有各個(gè)領(lǐng)域優(yōu)秀的小伙伴共同前行,大家如果想深入了解某個(gè)領(lǐng)域的具體實(shí)現(xiàn),可以隨時(shí)留言交流~!
審核編輯 黃宇
-
FLEX
+關(guān)注
關(guān)注
0文章
47瀏覽量
15285 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2397瀏覽量
43098
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
鴻蒙跨端實(shí)踐-JS虛擬機(jī)架構(gòu)實(shí)現(xiàn)
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>跨</b><b class='flag-5'>端</b><b class='flag-5'>實(shí)踐</b>-JS虛擬機(jī)架構(gòu)實(shí)現(xiàn)](https://file1.elecfans.com//web2/M00/08/BC/wKgZomb6SCqAaKbAAACBgMlQU1Y485.png)
揭秘動(dòng)態(tài)化跨端框架在鴻蒙系統(tǒng)下的高性能解決方案
![揭秘動(dòng)態(tài)化<b class='flag-5'>跨</b><b class='flag-5'>端</b>框架在<b class='flag-5'>鴻蒙</b>系統(tǒng)下的高性能解決<b class='flag-5'>方案</b>](https://file1.elecfans.com//web2/M00/09/F5/wKgaomcExxeAVKpeAALojfzrv6c694.png)
AIGC入門及鴻蒙入門
最佳天線實(shí)踐、布局指南以及天線調(diào)試程序
基于react-app配置移動(dòng)端自適應(yīng)—淘寶彈性布局方案
鴻蒙介紹
鴻蒙應(yīng)用ui布局
DC-DC降壓轉(zhuǎn)換器電路布局與設(shè)計(jì)和實(shí)踐
![DC-DC降壓轉(zhuǎn)換器電路<b class='flag-5'>布局</b>與設(shè)計(jì)和<b class='flag-5'>實(shí)踐</b>](https://file.elecfans.com/web1/M00/9C/45/pIYBAF0m962AbZOpAAAKxgtk2P0717.jpg)
鴻蒙強(qiáng)勢(shì)布局2021 庫克對(duì)蘋果做出戰(zhàn)略布局
![<b class='flag-5'>鴻蒙</b>強(qiáng)勢(shì)<b class='flag-5'>布局</b>2021 庫克對(duì)蘋果做出戰(zhàn)略<b class='flag-5'>布局</b>](https://file.elecfans.com/web1/M00/DB/B9/pIYBAGAHoimAItHUAAEURvqaj2I561.png)
HarmonyOS跨端分布式算力技術(shù)介紹
![HarmonyOS<b class='flag-5'>跨</b><b class='flag-5'>端</b>分布式算力技術(shù)<b class='flag-5'>介紹</b>](https://file.elecfans.com/web2/M00/1D/FB/pYYBAGGUwouARDc4AAAOMt0hI0Q336.png)
鴻蒙應(yīng)用如何喚起 QQ 安卓客戶端進(jìn)行授權(quán)
鴻蒙開發(fā):應(yīng)用組件跨設(shè)備交互(流轉(zhuǎn))【跨端遷移】
![<b class='flag-5'>鴻蒙</b>開發(fā):應(yīng)用組件<b class='flag-5'>跨</b>設(shè)備交互(流轉(zhuǎn))【<b class='flag-5'>跨</b><b class='flag-5'>端</b>遷移】](https://file1.elecfans.com/web2/M00/ED/97/wKgaomZoDo-ARaW5AABo08GI6Tw501.jpg)
鴻蒙跨端實(shí)踐-長列表解決方案和性能優(yōu)化
![<b class='flag-5'>鴻蒙</b><b class='flag-5'>跨</b><b class='flag-5'>端</b><b class='flag-5'>實(shí)踐</b>-長列表解決<b class='flag-5'>方案</b>和性能優(yōu)化](https://file1.elecfans.com//web2/M00/07/74/wKgZombxGB6ABSp1AAFjOcKituY611.png)
評(píng)論