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