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