一、布局簡(jiǎn)介
自適應(yīng)布局是通過(guò)設(shè)定元素與外部容器的相對(duì)關(guān)系實(shí)現(xiàn)的。當(dāng)外部容器大小、位置等發(fā)生變化時(shí),元素即可以根據(jù)相對(duì)關(guān)系自動(dòng)變化以適應(yīng)外部環(huán)境的變化。通常自適應(yīng)布局能根據(jù)vp/px變化進(jìn)行無(wú)級(jí)連續(xù)的變化。
- 自適應(yīng)布局常常需要借助Row組件、Column組件或Flex組件實(shí)現(xiàn)。

- 針對(duì)常見(jiàn)的開(kāi)發(fā)場(chǎng)景,方舟開(kāi)發(fā)框架提煉了七種自適應(yīng)布局能力,這些布局可以獨(dú)立使用,也可多種布局疊加使用。
| 自適應(yīng)布局能力 | 使用場(chǎng)景 | 實(shí)現(xiàn)方式 |
|---|---|---|
| 拉伸能力 | 容器組件尺寸發(fā)生變化時(shí),增加或減小的空間全部分配給容器組件內(nèi)指定區(qū)域。 | Flex布局的flexGrow和flexShrink屬性 |
| 均分能力 | 容器組件尺寸發(fā)生變化時(shí),增加或減小的空間均勻分配給容器組件內(nèi)所有空白區(qū)域。 | Row組件、Column組件或Flex組件的justifyContent屬性設(shè)置為FlexAlign.SpaceEvenly |
| 占比能力 | 子組件的寬或高按照預(yù)設(shè)的比例,隨容器組件發(fā)生變化。 | 基于通用屬性的兩種實(shí)現(xiàn)方式: 將子組件的寬高設(shè)置為父組件寬高的百分比 layoutWeight屬性 |
| 縮放能力 | 子組件的寬高按照預(yù)設(shè)的比例,隨容器組件發(fā)生變化,且變化過(guò)程中子組件的寬高比不變。 | 布局約束的aspectRatio屬性 |
| 延伸能力 | 容器組件內(nèi)的子組件,按照其在列表中的先后順序,隨容器組件尺寸變化顯示或隱藏。 | 基于容器組件的兩種實(shí)現(xiàn)方式: 通過(guò)List組件實(shí)現(xiàn) 通過(guò)Scroll組件配合Row組件或Column組件實(shí)現(xiàn) |
| 隱藏能力 | 容器組件內(nèi)的子組件,按照其預(yù)設(shè)的顯示優(yōu)先級(jí),隨容器組件尺寸變化顯示或隱藏。相同顯示優(yōu)先級(jí)的子組件同時(shí)顯示或隱藏。 | 布局約束的displayPriority屬性 |
| 折行能力 | 容器組件尺寸發(fā)生變化時(shí),如果布局方向尺寸不足以顯示完整內(nèi)容,自動(dòng)換行。 | Flex組件的wrap屬性設(shè)置為FlexWrap.Wrap |
二、自適應(yīng)拉伸
某單個(gè)內(nèi)容或某組內(nèi)容的顯示寬度不是固定值,而是通過(guò)相對(duì)參照物的方式來(lái)確定其顯示寬度。當(dāng)參照物的寬度發(fā)生變化時(shí),內(nèi)容或內(nèi)容間距的寬度隨之發(fā)生自適應(yīng)拉伸。
自適應(yīng)拉伸適用于文字、普通按鈕、間距等展示寬度靈活,對(duì)寬高比不敏感的內(nèi)容和內(nèi)容組合。
當(dāng)可能出現(xiàn)的拉伸寬度不足以顯示默認(rèn)內(nèi)容時(shí),應(yīng)根據(jù)場(chǎng)景選擇優(yōu)先保證內(nèi)容完整或者優(yōu)先保證其他內(nèi)容的屏效,并進(jìn)行截?cái)嗷驌Q行等組合適配。
1、左右拉伸
例如,列表開(kāi)關(guān)組合中,在窗口寬度變化時(shí),開(kāi)關(guān)控件固定寬度并相對(duì)列表的右邊緣位置固定,整個(gè)組合與文本寬度均自適應(yīng)變化。

示例代碼
@Entry
@Component
struct AdaptiveLayoutPage {
build() {
Column() {
Row() {
Text('健康使用手機(jī)')
.fontSize(16)
.height(22)
.fontWeight(FontWeight.Medium)
.lineHeight(22)
Blank() // 通過(guò)Blank組件實(shí)現(xiàn)拉伸能力
Toggle({ type: ToggleType.Switch })
.width(36)
.height(20)
}
.height(55)
.borderRadius(12)
.padding({ left: 15, right: 15 })
.backgroundColor('#FFFFFF')
.width('100%')//此處寬度變化時(shí),文字和開(kāi)關(guān)的尺寸固定,僅有中間空白區(qū)域(Blank組件)隨父容器尺寸變化而伸縮。
}
.height('100%')
.width('100%')
.padding(20)
.backgroundColor(Color.Gray)
}
}
2、均分拉伸
例如,在圖標(biāo)型網(wǎng)格中,當(dāng)窗口寬度變化時(shí),入口圖標(biāo)間距與圖標(biāo)離左右邊緣間距同時(shí)均等變化。

示例代碼
@Entry
@Component
struct AdaptiveLayoutPage {
readonly list: number [] = [0, 1, 2, 3]
build() {
Column() {
// 均勻分配父容器主軸方向的剩余空間
Row() {
ForEach(this.list, (item: number) => {
Column() {
Image($r("app.media.startIcon"))
.width(48)
.height(48)
.margin({ top: 8 })
Text('App name')
.width(64)
.height(30)
.lineHeight(15)
.fontSize(12)
.textAlign(TextAlign.Center)
.margin({ top: 8 })
.padding({ bottom: 15 })
}
.width(80)
.height(102)
.flexShrink(1)
})
}
.width('100%')//寬度變化時(shí),圖標(biāo)及文字的尺寸不變,圖標(biāo)間的間距及圖標(biāo)離左右邊緣的距離同時(shí)均等改變。
.justifyContent(FlexAlign.SpaceEvenly)
}
.height('100%')
.width('100%')
.backgroundColor(Color.White)
}
}
三、自適應(yīng)縮放
組件的顯示大小是固定比例,通過(guò)相對(duì)參照物的方式來(lái)確定其寬或高。當(dāng)參照物的大小發(fā)生變化時(shí),元素的大小隨之發(fā)生自適應(yīng)縮放。
自適應(yīng)縮放適用于圖片、圓形按鈕、banner、反應(yīng)真實(shí)物體形狀的圖像等必須保證寬高比的內(nèi)容。
不推薦將所有元素同時(shí)縮放、或某內(nèi)容放大過(guò)大超過(guò)屏幕50%。這將導(dǎo)致獲取信息量不增反減,不符合用戶預(yù)期。
1、完整縮放
例如,在寬度或高度變化時(shí),時(shí)鐘始終保證表盤(pán)完整展示并根據(jù)較短邊決定寬高。

示例代碼
@Entry
@Component
struct AdaptiveLayoutPage {
build() {
Column() {
Column() {
Column() {
Image($r("app.media.1"))
.width('100%')
.height('100%')
}
.aspectRatio(1)// 固定寬高比
}
.backgroundColor(Color.Pink)
.height(200)//這里寬或高改變是,圖片也隨之變化
.width(400)//這里寬或高改變是,圖片也隨之變化
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
.height('100%')
.width('100%')
.backgroundColor(Color.White)
}
}
2、占比縮放
例如,帶主體和背景的插畫(huà),畫(huà)面內(nèi)容根據(jù)寬度變化裁切,根據(jù)高度變化按50%比例縮放。

示例代碼
@Entry
@Component
struct AdaptiveLayoutPage {
build() {
Column() {
Row(){
}
.backgroundImage($r('app.media.banner'))
.backgroundImageSize(ImageSize.Cover)
.width('100%')
.layoutWeight(1)
Row(){
}
.backgroundColor(Color.White)
.width('100%')
.layoutWeight(1)
}
.height('100%')
.width('100%')
.backgroundColor(Color.Gray)
}
}
四、自適應(yīng)延伸
組件的顯示數(shù)量不是固定的,而是通過(guò)相對(duì)參照物的方式來(lái)確定其顯示數(shù)量。當(dāng)參照物的寬度發(fā)生變化時(shí),組件隨之發(fā)生自適應(yīng)延伸顯示更多數(shù)量。
自適應(yīng)延伸/隱藏適用于頁(yè)簽、操作塊、推薦欄目等具有相同交互層級(jí)且有更多數(shù)據(jù)可以填充的內(nèi)容。
注意:需要判斷因隱藏而不展示的內(nèi)容對(duì)功能完整性是否有影響,并考慮通過(guò)滑動(dòng)或“更多”按鈕提供查看使用該內(nèi)容的方式。
1、同功能內(nèi)容延伸
例如,子頁(yè)簽和可滑動(dòng)宮格在默認(rèn)寬度下通過(guò)露出最后內(nèi)容,提示右方有更多入口,在寬度變化時(shí),可在每個(gè)元素寬度不變、保持滑動(dòng)交互時(shí)顯示更多數(shù)量。

示例代碼
@Entry
@Component
struct AdaptiveLayoutPage {
readonly appList: number [] = [0, 1, 2, 3, 4, 5, 6, 7]
build() {
Column() {
Row({ space: 10 }) {
// 通過(guò)List組件實(shí)現(xiàn)隱藏能力
List({ space: 10 }) {
ForEach(this.appList, (item:number) => {
ListItem() {
Column() {
Image($r("app.media.startIcon"))
.width(48)
.height(48)
.margin({ top: 8 })
Text('App name')
.width(64)
.height(30)
.lineHeight(15)
.fontSize(12)
.textAlign(TextAlign.Center)
.margin({ top: 8 })
.padding({ bottom: 15 })
}.width(80).height(102)
}.width(80).height(102)
})
}
.padding({ top: 16, left: 10 })
.listDirection(Axis.Horizontal)
.width('100%')
.height(118)
.borderRadius(16)
.backgroundColor(Color.White)
}
.width('100%')//這里寬度變化,頁(yè)面中顯示的圖標(biāo)數(shù)量隨之發(fā)生改變。
}
.height('100%')
.width('100%')
.backgroundColor(Color.Gray)
}
}
2、不同功能內(nèi)容延伸或隱藏
例如,默認(rèn)處于同一排的不同音樂(lè)播放按鈕優(yōu)先級(jí)不同,在寬度變化時(shí)可延伸或隱藏低優(yōu)先級(jí)的按鈕,最大化適應(yīng)不同窗口尺寸。

示例代碼
@Entry
@Component
struct AdaptiveLayoutPage {
build() {
Column() {
Row({ space:24 }) {
Image($r("sys.media.ohos_ic_public_sound_off"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(1) // 布局優(yōu)先級(jí)
Image($r("sys.media.ohos_ic_public_play_last"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(2) // 布局優(yōu)先級(jí)
Image($r("sys.media.ohos_ic_public_pause"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(3) // 布局優(yōu)先級(jí)
Image($r("sys.media.ohos_ic_public_play_next"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(2) // 布局優(yōu)先級(jí)
Image($r("sys.media.ohos_ic_public_more"))
.width(48)
.height(48)
.objectFit(ImageFit.Contain)
.displayPriority(1) // 布局優(yōu)先級(jí)
}
.width('100%')//父容器寬度發(fā)生變化時(shí),其子元素按照預(yù)設(shè)的優(yōu)先級(jí)顯示或隱藏。
.height(96)
.borderRadius(16)
.backgroundColor('#FFFFFF')
.justifyContent(FlexAlign.Center)
.alignItems(VerticalAlign.Center)
}
.height('100%')
.width('100%')
.padding(10)
.backgroundColor(Color.Gray)
}
}
五、自適應(yīng)折行
定義了折行能力的組件,可以根據(jù)組件容器的可用空間,體現(xiàn)縱向布局或者橫向布局。
自適應(yīng)折行適用于頁(yè)簽、操作塊、內(nèi)容流等具有相同交互層級(jí),且希望保證類型和數(shù)量完整性的內(nèi)容。
自適應(yīng)布局對(duì)應(yīng)OpenHarmony系統(tǒng)提供的自適應(yīng)布局能力中的拉伸、均分、縮放、占比、延伸、隱藏、折行。
例如,在寬度足夠時(shí),操作塊位于同一行,在寬度變小時(shí),可根據(jù)內(nèi)容能顯示的寬度縱向排布。

示例代碼
@Entry
@Component
struct AdaptiveLayoutPage {
readonly searchList: string [] = ['搖滾', '九十年代金曲', '交響樂(lè)', '流行']
build() {
Column() {
// 通過(guò)Flex組件warp參數(shù)實(shí)現(xiàn)自適應(yīng)折行
Flex({
direction: FlexDirection.Row,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.Start,
wrap: FlexWrap.Wrap
}) {
ForEach(this.searchList, (item: Resource) => {
Text(item)
.padding({
top: 8,
bottom: 8,
left: 20,
right: 20
})
.margin(8)
.fontSize(16)
.fontWeight(500)
.borderRadius('50%')
.backgroundColor('#e1e3e5')
})
}
.backgroundColor('#FFFFFF')
.padding(10)
.width('100%')//當(dāng)父容器寬度發(fā)生變化,其中的內(nèi)容做自適應(yīng)換行。
.borderRadius(15)
}
.height('100%')
.width('100%')
.padding(10)
.backgroundColor(Color.Gray)
}
}