1# 响应式布局 2 3 4自适应布局可以保证窗口尺寸在一定范围内变化时,页面的显示是正常的。但是将窗口尺寸变化较大时(如窗口宽度从400vp变化为1000vp),仅仅依靠自适应布局可能出现图片异常放大或页面内容稀疏、留白过多等问题,此时就需要借助响应式布局能力调整页面结构。 5 6 7响应式布局是指页面内的元素可以根据特定的特征(如窗口宽度、屏幕方向等)自动变化以适应外部容器变化的布局能力。响应式布局中最常使用的特征是窗口宽度,可以将窗口宽度划分为不同的范围(下文中称为断点)。当窗口宽度从一个断点变化到另一个断点时,改变页面布局(如将页面内容从单列排布调整为双列排布甚至三列排布等)以获得更好的显示效果。 8 9 10当前OpenHarmony提供了如下三种响应式布局能力,后文中我们将依次展开介绍。 11 12 13 | 响应式布局能力 | 简介 | 14| -------- | -------- | 15| [断点](#断点) | 将窗口宽度划分为不同的范围(即断点),监听窗口尺寸变化,当断点改变时同步调整页面布局。 | 16| [媒体查询](#媒体查询) | 媒体查询支持监听窗口宽度、横竖屏、深浅色、设备类型等多种媒体特征,当媒体特征发生改变时同步调整页面布局。 | 17| [栅格布局](#栅格布局) | 栅格组件将其所在的区域划分为有规律的多列,通过调整不同断点下的栅格组件的参数以及其子组件占据的列数等,实现不同的布局效果。 | 18 19 20## 断点 21 22断点以应用窗口宽度为切入点,将应用窗口在宽度维度上分成了几个不同的区间即不同的断点,在不同的区间下,开发者可根据需要实现不同的页面布局效果。具体的断点如下所示。 23 24 | 断点名称 | 取值范围(vp) | 25| -------- | -------- | 26| xs | [0, 320) | 27| sm | [320, 520) | 28| md | [520, 840) | 29| lg | [840, +∞) | 30 31> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 32> - 以设备屏幕宽度作为参照物,也可以实现类似的效果。考虑到应用可能以非全屏窗口的形式显示,以应用窗口宽度为参照物更为通用。 33> 34> - 开发者可以根据实际使用场景决定适配哪些断点。如xs断点对应的一般是智能穿戴类设备,如果确定某页面不会在智能穿戴设备上显示,则可以不适配xs断点。 35> 36> - 可以根据实际需要在lg断点后面新增xl、xxl等断点,但注意新增断点会同时增加UX设计师及应用开发者的工作量,除非必要否则不建议盲目新增断点。 37 38OpenHarmony提供了多种方法,判断应用当前处于何种断点,进而可以调整应用的布局。常见的监听断点变化的方法如下所示: 39 40- 获取窗口对象并监听窗口尺寸变化 41 42- 通过媒体查询监听应用窗口尺寸变化 43 44- 借助栅格组件能力监听不同断点的变化 45 46本小节中,先介绍如何通过窗口对象监听断点变化,后续的媒体查询及栅格章节中,将进一步展开介绍另外两种方法。 47 48通过窗口对象监听断点变化的核心是获取窗口对象及注册窗口尺寸变化的回调函数。 49 501. 在Ability的[onWindowStageCreate](../../application-models/uiability-lifecycle.md)生命周期回调中,通过[窗口](../../reference/apis/js-apis-window.md)对象获取启动时的应用窗口宽度并注册回调函数监听窗口尺寸变化。将窗口尺寸的长度单位[由px换算为vp](../../key-features/multi-device-app-dev/visual-basics.md#视觉基础)后,即可基于前文中介绍的规则得到当前断点值,此时可以使用[状态变量](../../quick-start/arkts-state.md)记录当前的断点值方便后续使用。 51 52 ```ts 53 // MainAbility.ts 54 import window from '@ohos.window' 55 import display from '@ohos.display' 56 57 export default class MainAbility extends Ability { 58 private windowObj: window.Window 59 private curBp: string 60 ... 61 // 根据当前窗口尺寸更新断点 62 private updateBreakpoint(windowWidth) { 63 // 将长度的单位由px换算为vp 64 let windowWidthVp = windowWidth / (display.getDefaultDisplaySync().densityDPI / 160) 65 let newBp: string = '' 66 if (windowWidthVp < 320) { 67 newBp = 'xs' 68 } else if (windowWidthVp < 520) { 69 newBp = 'sm' 70 } else if (windowWidthVp < 840) { 71 newBp = 'md' 72 } else { 73 newBp = 'lg' 74 } 75 if (this.curBp !== newBp) { 76 this.curBp = newBp 77 // 使用状态变量记录当前断点值 78 AppStorage.SetOrCreate('currentBreakpoint', this.curBp) 79 } 80 } 81 82 onWindowStageCreate(windowStage: window.WindowStage) { 83 windowStage.getMainWindow().then((windowObj) => { 84 this.windowObj = windowObj 85 // 获取应用启动时的窗口尺寸 86 this.updateBreakpoint(windowObj.getWindowProperties().windowRect.width) 87 // 注册回调函数,监听窗口尺寸变化 88 windowObj.on('windowSizeChange', (windowSize)=>{ 89 this.updateBreakpoint(windowSize.width) 90 }) 91 }); 92 ... 93 } 94 95 // 窗口销毁时,取消窗口尺寸变化监听 96 onWindowStageDestroy() { 97 if (this.windowObj) { 98 this.windowObj.off('windowSizeChange') 99 } 100 } 101 ... 102 } 103 ``` 104 1052. 在页面中,获取及使用当前的断点。 106 107 ```ts 108 @Entry 109 @Component 110 struct Index { 111 @StorageProp('currentBreakpoint') curBp: string = 'sm' 112 113 build() { 114 Flex({justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center}) { 115 Text(this.curBp).fontSize(50).fontWeight(FontWeight.Medium) 116 } 117 .width('100%') 118 .height('100%') 119 } 120 } 121 ``` 122 1233. 运行及验证效果。 124 | | | | 125 | -------- | -------- | -------- | 126 | ![zh-cn_image_0000001336485520](figures/zh-cn_image_0000001336485520.jpg) | ![zh-cn_image_0000001386645965](figures/zh-cn_image_0000001386645965.jpg) | ![zh-cn_image_0000001386325621](figures/zh-cn_image_0000001386325621.jpg) | 127 128 129## 媒体查询 130 131 132在实际应用开发过程中,开发者常常需要针对不同类型设备或同一类型设备的不同状态来修改应用的样式。媒体查询提供了丰富的媒体特征监听能力,可以监听应用显示区域变化、横竖屏、深浅色、设备类型等等,因此在应用开发过程中使用的非常广泛。 133 134 135本小节仅介绍**媒体查询跟断点的结合**,即如何借助媒体查询能力,监听断点的变化,读者可以自行查阅官网中关于[媒体查询](../../ui/arkts-layout-development-media-query.md)的相关介绍了解更详细的用法。 136 137 138> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 139> 类Web开发范式,支持在js文件和css文件中使用媒体查询,请查看[js媒体查询](../../reference/apis/js-apis-mediaquery.md)和[css媒体查询](../../reference/arkui-js/js-components-common-mediaquery.md)了解详细用法。 140 141 142**示例:** 143 144 145通过媒体查询,监听应用窗口宽度变化,获取当前应用所处的断点值。 146 147 148 | | | | 149| -------- | -------- | -------- | 150| ![zh-cn_image_0000001336165712](figures/zh-cn_image_0000001336165712.jpg) | ![zh-cn_image_0000001386485617](figures/zh-cn_image_0000001386485617.jpg) | ![zh-cn_image_0000001386805569](figures/zh-cn_image_0000001386805569.jpg) | 151 152 153 154``` 155// common/breakpointsystem.ets 156// 对通过媒体查询监听断点的功能做简单的封装,方便后续使用 157import mediaquery from '@ohos.mediaquery'; 158 159export class BreakpointType<T> { 160 sm: T 161 md: T 162 lg: T 163 constructor(sm: T, md: T, lg: T) { 164 this.sm = sm 165 this.md = md 166 this.lg = lg 167 } 168 GetValue(currentBreakpoint: string) { 169 if (currentBreakpoint === 'sm') { 170 return this.sm 171 } 172 if (currentBreakpoint === 'md') { 173 return this.md 174 } 175 if (currentBreakpoint === 'lg') { 176 return this.lg 177 } 178 return undefined 179 } 180} 181 182export class BreakpointSystem { 183 private currentBreakpoint: string = 'md' 184 private smListener: mediaquery.MediaQueryListener 185 private mdListener: mediaquery.MediaQueryListener 186 private lgListener: mediaquery.MediaQueryListener 187 188 private updateCurrentBreakpoint(breakpoint: string) { 189 if (this.currentBreakpoint !== breakpoint) { 190 this.currentBreakpoint = breakpoint 191 AppStorage.Set<string>('currentBreakpoint', this.currentBreakpoint) 192 } 193 } 194 private isBreakpointSM = (mediaQueryResult) => { 195 if (mediaQueryResult.matches) { 196 this.updateCurrentBreakpoint('sm') 197 } 198 } 199 private isBreakpointMD = (mediaQueryResult) => { 200 if (mediaQueryResult.matches) { 201 this.updateCurrentBreakpoint('md') 202 } 203 } 204 private isBreakpointLG = (mediaQueryResult) => { 205 if (mediaQueryResult.matches) { 206 this.updateCurrentBreakpoint('lg') 207 } 208 } 209 210 public register() { 211 this.smListener = mediaquery.matchMediaSync("(320vp<width<520vp)") 212 this.smListener.on("change", this.isBreakpointSM) 213 this.mdListener = mediaquery.matchMediaSync("(520vp<width<840vp)") 214 this.mdListener.on("change", this.isBreakpointMD) 215 this.lgListener = mediaquery.matchMediaSync("(840vp<width)") 216 this.lgListener.on("change", this.isBreakpointLG) 217 } 218 219 public unregister() { 220 this.smListener.off("change", this.isBreakpointSM) 221 this.mdListener.off("change", this.isBreakpointMD) 222 this.lgListener.off("change", this.isBreakpointLG) 223 } 224} 225 226// MediaQuerySample.ets 227import { BreakpointSystem, BreakpointType } from '../common/breakpointsystem' 228 229@Entry 230@Component 231struct MediaQuerySample { 232 @StorageLink('currentBreakpoint') private currentBreakpoint: string = "md"; 233 @State private icon: Resource = $r('app.media.md') 234 private breakpointSystem: BreakpointSystem = new BreakpointSystem() 235 236 aboutToAppear() { 237 this.breakpointSystem.register() 238 } 239 240 aboutToDisappear() { 241 this.breakpointSystem.unregister() 242 } 243 build() { 244 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 245 Image(new BreakpointType($r('app.media.sm'), $r('app.media.md'), $r('app.media.lg')).GetValue(this.currentBreakpoint)) 246 .height(100) 247 .width(100) 248 .objectFit(ImageFit.Contain) 249 250 Text(this.currentBreakpoint) 251 .fontSize(24) 252 .margin(10) 253 } 254 .width('100%') 255 .height('100%') 256 } 257} 258``` 259 260 261## 栅格布局 262 263### 简介 264 265栅格是多设备场景下通用的辅助定位工具,通过将空间分割为有规律的栅格。栅格可以显著降低适配不同屏幕尺寸的设计及开发成本,使得整体设计和开发流程更有秩序和节奏感,同时也保证多设备上应用显示的协调性和一致性,提升用户体验。 266 267![zh-cn_image_0000001224173302](figures/zh-cn_image_0000001224173302.png) 268 269 栅格的样式由Margin、Gutter、Columns三个属性决定。 270- Margin是相对应用窗口、父容器的左右边缘的距离,决定了内容可展示的整体宽度。 271 272- Gutter是相邻的两个Column之间的距离,决定内容间的紧密程度。 273 274- Columns是栅格中的列数,其数值决定了内容的布局复杂度。 275 276单个Column的宽度是系统结合Margin、Gutter和Columns自动计算的,不需要也不允许开发者手动配置。 277 278栅格布局就是栅格结合了断点,实现栅格布局能力的组件叫栅格组件。在实际使用场景中,可以根据需要配置不同断点下栅格组件中元素占据的列数,同时也可以调整Margin、Gutter、Columns的取值,从而实现不同的布局效果。 279 280 | sm断点 | md断点 | 281| -------- | -------- | 282| ![zh-cn_image_0000001336486244](figures/zh-cn_image_0000001336486244.jpg) | ![zh-cn_image_0000001386646685](figures/zh-cn_image_0000001386646685.jpg) | 283 284> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 285> - ArkUI在API 9对栅格组件做了重构,推出了新的栅格组件[GridRow](../../reference/arkui-ts/ts-container-gridrow.md)和[GridCol](../../reference/arkui-ts/ts-container-gridcol.md),同时原有的[GridContainer组件](../../reference/arkui-ts/ts-container-gridcontainer.md)及[栅格设置](../../reference/arkui-ts/ts-universal-attributes-grid.md)已经废弃。 286> 287> - 本文中提到的栅格组件,如无特别说明,都是指GridRow和GridCol组件。 288 289 290### 栅格组件的断点 291 292栅格组件提供了丰富的断点定制能力。 293 294**(一)开发者可以修改断点的取值范围,支持启用最多6个断点。** 295 296- 基于本文断点小节介绍的推荐值,栅格组件默认提供xs、sm、md、lg四个断点。 297 298- 栅格组件支持开发者修改断点的取值范围,除了默认的四个断点,还支持开发者启用xl和xxl两个额外的断点。 299 300> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 301> 断点并非越多越好,通常每个断点都需要开发者“精心适配”以达到最佳显示效果。 302 303**示例1:** 304 305修改默认的断点范围,同时启用xl和xxl断点。 306 307图片右下角显示了当前设备屏幕的尺寸(即应用窗口尺寸),可以看到随着窗口尺寸发生变化,栅格的断点也相应发生了改变。(为了便于理解,下图中将设备的DPI设置为160,此时1vp=1px) 308 309![window3](figures/window3.gif) 310 311 312``` 313@Entry 314@Component 315struct GridRowSample1 { 316 @State private currentBreakpoint: string = 'unknown' 317 build() { 318 // 修改断点的取值范围同时启用更多断点,注意,修改的断点值后面必须加上vp单位。 319 GridRow({breakpoints: {value: ['600vp', '700vp', '800vp', '900vp', '1000vp'], 320 reference: BreakpointsReference.WindowSize}}) { 321 GridCol({span:{xs: 12, sm: 12, md: 12, lg:12, xl: 12, xxl:12}}) { 322 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 323 Text(this.currentBreakpoint).fontSize(50).fontWeight(FontWeight.Medium) 324 } 325 } 326 }.onBreakpointChange((currentBreakpoint: string) => { 327 this.currentBreakpoint = currentBreakpoint 328 }) 329 } 330} 331``` 332 333**(二)栅格断点默认以窗口宽度为参照物,同时还允许开发者配置为以栅格组件本身的宽度为参照物。** 334 335栅格既可以用于页面整体布局的场景,也可以用于页面局部布局的场景。考虑到在实际场景中,存在应用窗口尺寸不变但是局部区域尺寸发生了变化的情况,栅格组件支持以自身宽度为参照物响应断点变化具有更大的灵活性。 336 337**示例2:** 338 339以栅格组件宽度为参考物响应断点变化。满足窗口尺寸不变,而部分内容区需要做响应式变化的场景。 340 341为了便于理解,下图中自定义预览器的设备屏幕宽度设置为650vp。示例代码中将侧边栏的变化范围控制在[100vp, 600vp],那么右侧的栅格组件宽度相对应在[550vp, 50vp]之间变化。根据代码中对栅格断点的配置,栅格组件宽度发生变化时,其断点相应的发生改变。 342 343![component](figures/component.gif) 344 345 346``` 347@Entry 348@Component 349struct GridRowSample2 { 350 @State private currentBreakpoint: string = 'unknown'; 351 build() { 352 // 用户可以通过拖拽侧边栏组件中的分隔线,调整侧边栏和内容区的宽度。 353 SideBarContainer(SideBarContainerType.Embed) 354 { 355 // 侧边栏,尺寸变化范围 [100vp, 600vp] 356 Column(){}.width('100%').backgroundColor('#19000000') 357 358 // 内容区,尺寸变化范围 [550vp, 50vp] 359 GridRow({breakpoints: {value: ['100vp', '200vp', '300vp', '400vp', '500vp'], 360 reference: BreakpointsReference.ComponentSize}}) { 361 GridCol({span:{xs: 12, sm: 12, md: 12, lg:12, xl: 12, xxl:12}}) { 362 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 363 Text(this.currentBreakpoint).fontSize(50).fontWeight(FontWeight.Medium) 364 } 365 } 366 }.onBreakpointChange((currentBreakpoint: string) => { 367 this.currentBreakpoint = currentBreakpoint; 368 }).width('100%') 369 } 370 // 侧边栏拖拽到最小宽度时,不自动隐藏 371 .autoHide(false) 372 .sideBarWidth(100) 373 // 侧边栏的最小宽度 374 .minSideBarWidth(100) 375 // 侧边栏的最大宽度 376 .maxSideBarWidth(600) 377 } 378} 379``` 380 381**(三)栅格组件的断点发生变化时,会通过onBreakPointChange事件通知开发者。** 382 383在之前的两个例子中,已经演示了onBreakpointChange事件的用法,此处不再赘述。 384 385 386### 栅格组件的columns、gutter和margin 387 388 389栅格组件columns默认为12列,gutter默认为0,同时支持开发者根据实际需要定义不同断点下的columns数量以及gutter长度。特别的,在栅格组件实际使用过程中,常常会发生多个元素占据的列数相加超过总列数而折行的场景。栅格组件还允许开发者分别定义水平方向的gutter(相邻两列之间的间距)和垂直方向的gutter(折行时相邻两行之间的间距)。 390 391 392 考虑到[组件通用属性](../../reference/arkui-ts/ts-universal-attributes-size.md)中已经有margin和padding,栅格组件不再单独提供额外的margin属性,直接使用通用属性即可。借助margin或者padding属性,均可以控制栅格组件与父容器左右边缘的距离,但是二者也存在一些差异: 393- margin区域在栅格组件的边界外,padding区域在栅格组件的边界内。 394 395- 栅格组件的backgroundColor会影响padding区域,但不会影响margin区域。 396 397 398总的来讲,margin在组件外而padding在组件内,开发者可以根据实际需要进行选择及实现目标效果。 399 400**示例3:** 401 402 403不同断点下,定义不同的columns和gutter。 404 405 406 | sm | md | lg | 407| -------- | -------- | -------- | 408| ![zh-cn_image_0000001386807405](figures/zh-cn_image_0000001386807405.jpg) | ![zh-cn_image_0000001336167540](figures/zh-cn_image_0000001336167540.jpg) | ![zh-cn_image_0000001386487457](figures/zh-cn_image_0000001386487457.jpg) | 409 410 411 412``` 413@Entry 414@Component 415struct GridRowSample3 { 416 private bgColors: ResourceColor[] = [ 417 $r('sys.color.ohos_id_color_palette_aux1'), 418 $r('sys.color.ohos_id_color_palette_aux2'), 419 $r('sys.color.ohos_id_color_palette_aux3'), 420 $r('sys.color.ohos_id_color_palette_aux4'), 421 $r('sys.color.ohos_id_color_palette_aux5'), 422 $r('sys.color.ohos_id_color_palette_aux6') 423 ] 424 build() { 425 // 配置不同断点下columns和gutter的取值 426 GridRow({columns: {sm: 4, md: 8, lg: 12}, 427 gutter: {x: {sm: 8, md: 16, lg: 24}, y: {sm: 8, md: 16, lg: 24}}}) { 428 ForEach(this.bgColors, (bgColor)=>{ 429 GridCol({span: {sm: 2, md: 2, lg: 2}}) { 430 Row().backgroundColor(bgColor).height(30) 431 } 432 }) 433 } 434 } 435} 436``` 437 438 439**示例4:** 440 441 442通过通用属性margin或者padding,均可以控制栅格组件与其父容器左右两侧的距离,但padding区域计算在栅格组件内而margin区域计算在栅格组件外。此外,借助onBreakpointChange事件,还可以改变不同断点下margin或padding值。 443 444 445![zh-cn_image_0000001336327452](figures/zh-cn_image_0000001336327452.png) 446 447 448 449``` 450@Entry 451@Component 452struct GridRowSample4 { 453 @State private gridMargin: number = 0 454 build() { 455 Column() { 456 Row().width('100%').height(30) 457 458 // 使用padding控制栅格左右间距 459 GridRow() { 460 GridCol({span:{xs: 12, sm: 12, md: 12, lg:12}}) { 461 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 462 Text("padding").fontSize(24).fontWeight(FontWeight.Medium) 463 }.backgroundColor('#19000000') 464 } 465 } 466 .height(50) 467 .borderWidth(2) 468 .borderColor('#F1CCB8') 469 .padding({left: this.gridMargin, right: this.gridMargin}) 470 // 借助断点变化事件配置不同断点下栅格组件的左右间距值 471 .onBreakpointChange((currentBreakpoint: string) => { 472 if (currentBreakpoint === 'lg' || currentBreakpoint === 'md') { 473 this.gridMargin = 24 474 } else { 475 this.gridMargin = 12 476 } 477 }) 478 479 Row().width('100%').height(30) 480 481 // 使用margin控制栅格左右间距 482 GridRow() { 483 GridCol({span:{xs: 12, sm: 12, md: 12, lg:12}}) { 484 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 485 Text("margin").fontSize(24).fontWeight(FontWeight.Medium) 486 }.backgroundColor('#19000000') 487 } 488 } 489 .height(50) 490 .borderWidth(2) 491 .borderColor('#F1CCB8') 492 .margin({left: this.gridMargin, right: this.gridMargin}) 493 } 494 } 495} 496``` 497 498 499### 栅格组件的span、offset和order 500 501 502栅格组件(GridRow)的直接孩子节点只可以是栅格子组件(GridCol),GridCol组件支持配置span、offset和order三个参数。这三个参数的取值按照"xs -> sm -> md -> lg -> xl -> xxl"的向后方向具有继承性(不支持向前方向的继承性),例如将sm断点下span的值配置为3,不配置md断点下span的值,则md断点下span的取值也是3。 503 504 505 | 参数名 | 类型 | 必填 | 默认值 | 说明 | 506| -------- | -------- | -------- | -------- | -------- | 507| span | {xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?:number} | 是 | - | 在栅格中占据的列数。span为0,意味着该元素既不参与布局计算,也不会被渲染。 | 508| offset | {xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?:number} | 否 | 0 | 相对于前一个栅格子组件偏移的列数。 | 509| order | {xs?: number, sm?: number, md?: number, lg?: number, xl?: number, xxl?:number} | 否 | 0 | 元素的序号,根据栅格子组件的序号,从小到大对栅格子组件做排序。 | 510 511 512**示例5:** 513 514 515通过span参数配置GridCol在不同断点下占据不同的列数。特别的,将md断点下和6的span配置为0,这样在md断点下3和6不会渲染和显示。 516 517 518 | sm | md | lg | 519| -------- | -------- | -------- | 520| ![zh-cn_image_0000001336487884](figures/zh-cn_image_0000001336487884.jpg) | ![zh-cn_image_0000001386648317](figures/zh-cn_image_0000001386648317.jpg) | ![zh-cn_image_0000001386327997](figures/zh-cn_image_0000001386327997.jpg) | 521 522 523 524``` 525@Entry 526@Component 527struct GridRowSample5 { 528 private elements: Object[] = [ 529 {'index': 1, 'color': $r('sys.color.ohos_id_color_palette_aux1')}, 530 {'index': 2, 'color': $r('sys.color.ohos_id_color_palette_aux2')}, 531 {'index': 3, 'color': $r('sys.color.ohos_id_color_palette_aux3')}, 532 {'index': 4, 'color': $r('sys.color.ohos_id_color_palette_aux4')}, 533 {'index': 5, 'color': $r('sys.color.ohos_id_color_palette_aux5')}, 534 {'index': 6, 'color': $r('sys.color.ohos_id_color_palette_aux6')}, 535 ] 536 537 build() { 538 GridRow() { 539 ForEach(this.elements, (item)=>{ 540 GridCol({span: {sm: 6, md: (item.index % 3 === 0) ? 0 : 4, lg: 3}}) { 541 Row() { 542 Text('' + item.index).fontSize(24) 543 } 544 .justifyContent(FlexAlign.Center) 545 .backgroundColor(item.color).height(30) 546 } 547 }) 548 } 549 } 550} 551``` 552 553 554**示例6:** 555 556 557通过offset参数,配置GridCol相对其前一个兄弟间隔的列数。 558 559 560 | sm | md | lg | 561| -------- | -------- | -------- | 562| ![zh-cn_image_0000001386807873](figures/zh-cn_image_0000001386807873.jpg) | ![zh-cn_image_0000001336168020](figures/zh-cn_image_0000001336168020.jpg) | ![zh-cn_image_0000001386487913](figures/zh-cn_image_0000001386487913.jpg) | 563 564 565 566``` 567@Entry 568@Component 569struct GridRowSample6 { 570 private elements: Object[] = [ 571 {'index': 1, 'color': $r('sys.color.ohos_id_color_palette_aux1')}, 572 {'index': 2, 'color': $r('sys.color.ohos_id_color_palette_aux2')}, 573 {'index': 3, 'color': $r('sys.color.ohos_id_color_palette_aux3')}, 574 {'index': 4, 'color': $r('sys.color.ohos_id_color_palette_aux4')}, 575 {'index': 5, 'color': $r('sys.color.ohos_id_color_palette_aux5')}, 576 {'index': 6, 'color': $r('sys.color.ohos_id_color_palette_aux6')}, 577 ] 578 579 build() { 580 GridRow() { 581 ForEach(this.elements, (item)=>{ 582 GridCol({span: {sm: 6, md: 4, lg: 3}, offset: {sm: 0, md: 2, lg: 1} }) { 583 Row() { 584 Text('' + item.index).fontSize(24) 585 } 586 .justifyContent(FlexAlign.Center) 587 .backgroundColor(item.color).height(30) 588 } 589 }) 590 } 591 } 592} 593``` 594 595 596**示例7:** 597 598 599通过order属性,控制GridCol的顺序。在sm和md断点下,按照1至6的顺序排列显示;在lg断点下,按照6至1的顺序排列显示。 600 601 602 | sm | md | lg | 603| -------- | -------- | -------- | 604| ![zh-cn_image_0000001336327916](figures/zh-cn_image_0000001336327916.jpg) | ![zh-cn_image_0000001336008356](figures/zh-cn_image_0000001336008356.jpg) | ![zh-cn_image_0000001336487888](figures/zh-cn_image_0000001336487888.jpg) | 605 606 607 608``` 609@Entry 610@Component 611struct GridRowSample7 { 612 private elements: Object[] = [ 613 {'index': 1, 'color': $r('sys.color.ohos_id_color_palette_aux1')}, 614 {'index': 2, 'color': $r('sys.color.ohos_id_color_palette_aux2')}, 615 {'index': 3, 'color': $r('sys.color.ohos_id_color_palette_aux3')}, 616 {'index': 4, 'color': $r('sys.color.ohos_id_color_palette_aux4')}, 617 {'index': 5, 'color': $r('sys.color.ohos_id_color_palette_aux5')}, 618 {'index': 6, 'color': $r('sys.color.ohos_id_color_palette_aux6')}, 619 ] 620 621 build() { 622 GridRow() { 623 ForEach(this.elements, (item)=>{ 624 GridCol({span: {sm: 6, md: 4, lg: 3}, order: {lg: (6-item.index)}}) { 625 Row() { 626 Text('' + item.index).fontSize(24) 627 } 628 .justifyContent(FlexAlign.Center) 629 .backgroundColor(item.color).height(30) 630 } 631 }) 632 } 633 } 634} 635``` 636 637 638**示例8:** 639 640 641仅配置sm和lg断点下span、offset和order参数的值,则md断点下这三个参数的取值与sm断点相同(按照“sm->md->lg”的向后方向继承)。 642 643 644 | sm | md | lg | 645| -------- | -------- | -------- | 646| ![zh-cn_image_0000001386648321](figures/zh-cn_image_0000001386648321.jpg) | ![zh-cn_image_0000001386328001](figures/zh-cn_image_0000001386328001.jpg) | ![zh-cn_image_0000001386807877](figures/zh-cn_image_0000001386807877.jpg) | 647 648 649 650``` 651@Entry 652@Component 653struct GridRowSample8 { 654 private elements: Object[] = [ 655 {'index': 1, 'color': $r('sys.color.ohos_id_color_palette_aux1')}, 656 {'index': 2, 'color': $r('sys.color.ohos_id_color_palette_aux2')}, 657 {'index': 3, 'color': $r('sys.color.ohos_id_color_palette_aux3')}, 658 {'index': 4, 'color': $r('sys.color.ohos_id_color_palette_aux4')}, 659 {'index': 5, 'color': $r('sys.color.ohos_id_color_palette_aux5')}, 660 {'index': 6, 'color': $r('sys.color.ohos_id_color_palette_aux6')}, 661 ] 662 663 build() { 664 GridRow() { 665 ForEach(this.elements, (item)=>{ 666 // 不配置md断点下三个参数的值,则其取值与sm断点相同 667 GridCol({span: {sm:4, lg: 3}, offset: {sm: 2, lg: 1}, 668 order: {sm: (6-item.index), lg: item.index}}) { 669 Row() { 670 Text('' + item.index).fontSize(24) 671 } 672 .justifyContent(FlexAlign.Center) 673 .backgroundColor(item.color).height(30) 674 } 675 }) 676 } 677 } 678} 679``` 680 681 682### 栅格组件的嵌套使用 683 684栅格组件可以嵌套使用以满足复杂场景的需要。 685 686**示例9:** 687 688 | sm | md | lg | 689| -------- | -------- | -------- | 690| ![zh-cn_image_0000001336338670](figures/zh-cn_image_0000001336338670.jpg) | ![zh-cn_image_0000001336019094](figures/zh-cn_image_0000001336019094.jpg) | ![zh-cn_image_0000001336498646](figures/zh-cn_image_0000001336498646.jpg) | 691 692 693``` 694@Entry 695@Component 696struct GridRowSample9 { 697 private elements: Object[] = [ 698 {'index': 1, 'color': $r('sys.color.ohos_id_color_palette_aux1')}, 699 {'index': 2, 'color': $r('sys.color.ohos_id_color_palette_aux2')}, 700 {'index': 3, 'color': $r('sys.color.ohos_id_color_palette_aux3')}, 701 {'index': 4, 'color': $r('sys.color.ohos_id_color_palette_aux4')}, 702 {'index': 5, 'color': $r('sys.color.ohos_id_color_palette_aux5')}, 703 {'index': 6, 'color': $r('sys.color.ohos_id_color_palette_aux6')}, 704 ] 705 706 build() { 707 GridRow() { 708 GridCol({span: {sm: 12, md: 10, lg: 8}, offset: {sm: 0, md: 1, lg: 2}}) { 709 GridRow() { 710 ForEach(this.elements, (item)=>{ 711 GridCol({span: {sm: 6, md: 4, lg: 3}}) { 712 Row() { 713 Text('' + item.index).fontSize(24) 714 } 715 .justifyContent(FlexAlign.Center) 716 .backgroundColor(item.color).height(30) 717 } 718 }) 719 } 720 .backgroundColor('#19000000') 721 .height('100%') 722 } 723 } 724 } 725} 726``` 727 728 729### 总结 730 731如前所述,栅格组件提供了丰富的自定义能力,功能异常灵活和强大。只需要明确栅格在不同断点下的Columns、Margin、Gutter及span等参数,即可确定最终布局,无需关心具体的设备类型及设备状态(如横竖屏)等。栅格可以节约设计团队与开发团队的沟通成本,提升整体开发效率。 732 733