• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 -&gt; sm -&gt; md -&gt; lg -&gt; xl -&gt; xxl"的向后方向具有继承性(不支持向前方向的继承性),例如将sm断点下span的值配置为3,不配置md断点下span的值,则md断点下span的取值也是3。
503
504
505  | 参数名 | 类型 | 必填 | 默认值 | 说明 |
506| -------- | -------- | -------- | -------- | -------- |
507| span | {xs?:&nbsp;number,&nbsp;sm?:&nbsp;number,&nbsp;md?:&nbsp;number,&nbsp;lg?:&nbsp;number,&nbsp;xl?:&nbsp;number,&nbsp;xxl?:number} | 是 | - | 在栅格中占据的列数。span为0,意味着该元素既不参与布局计算,也不会被渲染。 |
508| offset | {xs?:&nbsp;number,&nbsp;sm?:&nbsp;number,&nbsp;md?:&nbsp;number,&nbsp;lg?:&nbsp;number,&nbsp;xl?:&nbsp;number,&nbsp;xxl?:number} | 否 | 0 | 相对于前一个栅格子组件偏移的列数。 |
509| order | {xs?:&nbsp;number,&nbsp;sm?:&nbsp;number,&nbsp;md?:&nbsp;number,&nbsp;lg?:&nbsp;number,&nbsp;xl?:&nbsp;number,&nbsp;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-&gt;md-&gt;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