• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   | ![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) |
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| ![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) |
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![zh-cn_image_0000001224173302](figures/zh-cn_image_0000001224173302.png)
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| ![zh-cn_image_0000001336486244](figures/zh-cn_image_0000001336486244.jpg) | ![zh-cn_image_0000001386646685](figures/zh-cn_image_0000001386646685.jpg) |
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![window3](figures/window3.gif)
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![component](figures/component_example2.gif)
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| ![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) |
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![zh-cn_image_0000001336327452](figures/zh-cn_image_0000001336327452.png)
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 -&gt; sm -&gt; md -&gt; lg -&gt; xl -&gt; xxl"的向后方向具有继承性(不支持向前方向的继承性),例如将sm断点下span的值配置为3,不配置md断点下span的值,则md断点下span的取值也是3。
560
561
562  | 参数名 | 类型 | 必填 | 默认值 | 说明 |
563| -------- | -------- | -------- | -------- | -------- |
564| 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,意味着该元素既不参与布局计算,也不会被渲染。 |
565| offset | {xs?:&nbsp;number,&nbsp;sm?:&nbsp;number,&nbsp;md?:&nbsp;number,&nbsp;lg?:&nbsp;number,&nbsp;xl?:&nbsp;number,&nbsp;xxl?:number} | 否 | 0 | 相对于前一个栅格子组件偏移的列数。 |
566| order | {xs?:&nbsp;number,&nbsp;sm?:&nbsp;number,&nbsp;md?:&nbsp;number,&nbsp;lg?:&nbsp;number,&nbsp;xl?:&nbsp;number,&nbsp;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| ![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) |
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| ![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) |
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| ![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) |
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-&gt;md-&gt;lg”的向后方向继承)。
711
712
713  | sm | md | lg |
714| -------- | -------- | -------- |
715| ![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) |
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| ![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) |
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