• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Grid
2
3网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
4
5>  **说明:**
6>
7>  该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
8
9
10## 子组件
11
12仅支持[GridItem](ts-container-griditem.md)子组件。
13
14>  **说明:**
15>
16>  Grid子组件的索引值计算规则:
17>
18>  按子组件的顺序依次递增。
19>
20>  if/else语句中,只有条件成立分支内的子组件会参与索引值计算,条件不成立分支内的子组件不计算索引值。
21>
22>  ForEach/LazyForEach语句中,会计算展开所有子节点索引值。
23>
24>  [if/else](../../../quick-start/arkts-rendering-control-ifelse.md)、[ForEach](../../../quick-start/arkts-rendering-control-foreach.md)和[LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md)发生变化以后,会更新子节点索引值。
25>
26>  Grid子组件的visibility属性设置为Hidden或None时依然会计算索引值。
27>
28>  Grid子组件的visibility属性设置为None时不显示,但依然会占用子组件对应的网格。
29>
30>  Grid子组件设置position属性,会占用子组件对应的网格,子组件将显示在相对Grid左上角偏移position的位置。该子组件不会随其对应网格滚动,在对应网格滑出Grid显示范围外后不显示。
31>
32>  当Grid子组件之间留有空隙时,会根据当前的展示区域尽可能填补空隙,因此GridItem可能会随着网格滚动而改变相对位置。
33
34## 接口
35
36Grid(scroller?: Scroller, layoutOptions?: GridLayoutOptions)
37
38**参数:**
39
40| 参数名   | 参数类型                                    | 必填 | 参数描述                                                     |
41| -------- | ------------------------------------------- | ---- | ------------------------------------------------------------ |
42| scroller | [Scroller](ts-container-scroll.md#scroller) | 否   | 可滚动组件的控制器。用于与可滚动组件进行绑定。<br/>**说明:** <br/>不允许和其他滚动类组件,如:[List](ts-container-list.md)、[Grid](ts-container-grid.md)、[Scroll](ts-container-scroll.md)等绑定同一个滚动控制对象。 |
43| layoutOptions<sup>10+</sup> | [GridLayoutOptions](#gridlayoutoptions10) | 否 | 滚动Grid布局选项。 |
44
45## GridLayoutOptions<sup>10+</sup>
46
47布局选项。其中,irregularIndexes和onGetIrregularSizeByIndex可对仅设置rowsTemplate或columnsTemplate的Grid使用,可以指定一个index数组,并为其中的index对应的GridItem设置其占据的行数与列数,使用方法参见示例3;onGetRectByIndex可对同时设置rowsTemplate和columnsTemplate的Grid使用,为指定的index对应的GridItem设置位置和大小,使用方法参见示例1。
48
49**参数:**
50
51| 名称    | 类型      | 必填   | 描述                    |
52| ----- | ------- | ---- | --------------------- |
53| regularSize  | [number, number]  | 是    | 大小规则的GridItem在Grid中占的行数和列数,只支持占1行1列即[1, 1]。   |
54| irregularIndexes | number[] | 否    | 指定的GridItem索引在Grid中的大小是不规则的。当不设置onGetIrregularSizeByIndex时,irregularIndexes中GridItem的默认大小为垂直滚动Grid的一整行或水平滚动Grid的一整列。 |
55| onGetIrregularSizeByIndex | (index: number) => [number, number] | 否    | 配合irregularIndexes使用,设置不规则GridItem占用的行数和列数。开发者可为irregularIndexes中指明的index对应的GridItem设置占用的行数和列数。垂直滚动Grid不支持GridItem占多行,水平滚动Grid不支持GridItem占多列。 |
56| onGetRectByIndex<sup>11+</sup> | (index: number) => [number, number,number,number] | 否  | 设置指定索引index对应的GridItem的位置及大小[rowStart,columnStart,rowSpan,columnSpan]。 <br/>其中rowStart为行起始位置,columnStart为列起始位置,无单位。 <br/>rowSpan为GridItem占用的行数,columnSpan为GridItem占用的列数,无单位。 <br/>rowStart和columnStart取大于等于0的自然数,若取负数时,rowStart和columnStart默认为0。 <br/>rowSpan和columnSpan取大于等于1的自然数,若取小数则向下取整,若小于1则按1计算。<br/>**说明:** <br/>第一种情况:某个GridItem发现给它指定的起始位置被占据了,则从起始位置[0,0]开始按顺序从左到右,从上到下寻找起始的放置位置。<br/>第二种情况:如果起始位置没有被占据,但其他位置被占据了,无法显示全部的GridItem大小,则只会布局一部分。 |
57
58## 属性
59
60除支持[通用属性](ts-universal-attributes-size.md)外,还支持以下属性:
61
62| 名称 | 参数类型 | 描述 |
63| -------- | -------- | -------- |
64| columnsTemplate | string | 设置当前网格布局列的数量或最小列宽值,不设置时默认1列。<br/>例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'&nbsp;是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。<br/>columnsTemplate('repeat(auto-fit, track-size)')是设置最小列宽值为track-size,自动计算列数和实际列宽。<br/>columnsTemplate('repeat(auto-fill, track-size)')是设置固定列宽值为track-size,自动计算列数。<br/>其中repeat、auto-fit、auto-fill为关键字。track-size为列宽,支持的单位包括px、vp、%或有效数字,默认单位为vp,track-size至少包括一个有效列宽。<br/>**说明:** <br/>设置为'0fr'时,该列的列宽为0,不显示GridItem。设置为其他非法值时,GridItem显示为固定1列。 |
65| rowsTemplate | string | 设置当前网格布局行的数量或最小行高值,不设置时默认1行。<br/>例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。<br/>rowsTemplate('repeat(auto-fit, track-size)')是设置最小行高值为track-size,自动计算行数和实际行高。<br/>rowsTemplate('repeat(auto-fill, track-size)')是设置固定行高值为track-size,自动计算行数。<br/>其中repeat、auto-fit、auto-fill为关键字。track-size为行高,支持的单位包括px、vp、%或有效数字,默认单位为vp,track-size至少包括一个有效行高。<br/>**说明:** <br/>设置为'0fr',则这一行的行宽为0,这一行GridItem不显示。设置为其他非法值,按固定1行处理。 |
66| columnsGap | [Length](ts-types.md#length) | 设置列与列的间距。<br/>默认值:0<br/>**说明:** <br/>设置为小于0的值时,按默认值显示。 |
67| rowsGap | [Length](ts-types.md#length) | 设置行与行的间距。<br/>默认值:0<br/>**说明:** <br/>设置为小于0的值时,按默认值显示。 |
68| scrollBar      | [BarState](ts-appendix-enums.md#barstate) | 设置滚动条状态。<br/>默认值:BarState.Off<br/>**说明:** <br/>API version 9及以下版本默认值为BarState.Off,API version 10的默认值为BarState.Auto。 |
69| scrollBarColor | string&nbsp;\|&nbsp;number&nbsp;\|&nbsp;[Color](ts-appendix-enums.md#color) | 设置滚动条的颜色。 |
70| scrollBarWidth | string \| number    | 设置滚动条的宽度。宽度设置后,滚动条正常状态和按压状态宽度均为滚动条的宽度值。<br/>默认值:4<br/>单位:vp |
71| cachedCount | number                                   | 设置预加载的GridItem的数量,只在[LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md)中生效。具体使用可参考[减少应用白块说明](../../../performance/arkts-performance-improvement-recommendation.md#减少应用滑动白块)。<br/>默认值:1<br/>**说明:** <br>设置缓存后会在Grid显示区域上下各缓存cachedCount*列数个GridItem。<br/>[LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md)超出显示和缓存范围的GridItem会被释放。<br/>设置为小于0的值时,按默认值显示。 |
72| editMode <sup>8+</sup>                   | boolean | 设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部[GridItem](ts-container-griditem.md)。<br/>默认值:false |
73| layoutDirection<sup>8+</sup>             | [GridDirection](#griddirection8枚举说明) | 设置布局的主轴方向。<br/>默认值:GridDirection.Row |
74| maxCount<sup>8+</sup> | number  | 当layoutDirection是Row/RowReverse时,表示可显示的最大列数<br/>当layoutDirection是Column/ColumnReverse时,表示可显示的最大行数。<br/>默认值:Infinity<br/>**说明:** <br/>当maxCount小于minCount时,maxCount和minCount都按默认值处理。<br/>设置为小于1的值时,按默认值显示。 |
75| minCount<sup>8+</sup> | number  | 当layoutDirection是Row/RowReverse时,表示可显示的最小列数。<br/>当layoutDirection是Column/ColumnReverse时,表示可显示的最小行数。<br/>默认值:1<br/>**说明:** <br/>设置为小于1的值时,按默认值显示。 |
76| cellLength<sup>8+</sup> | number  | 当layoutDirection是Row/RowReverse时,表示一行的高度。<br/>当layoutDirection是Column/ColumnReverse时,表示一列的宽度。<br/>默认值:第一个元素的大小 |
77| multiSelectable<sup>8+</sup> | boolean | 是否开启鼠标框选。<br/>默认值:false<br/>-&nbsp;false:关闭框选。<br/>-&nbsp;true:开启框选。<br/>**说明:**<br/> 开启框选后,可以配合Griditem的selected属性和onSelect事件获取GridItem的选中状态,还可以设置[选中态样式](./ts-universal-attributes-polymorphic-style.md)(无默认选中样式)。 |
78| supportAnimation<sup>8+</sup> | boolean | 是否支持动画。当前支持GridItem拖拽动画。<br/>默认值:false<br/>**说明:**<br/> 仅在滚动模式下(只设置rowsTemplate、columnsTemplate其中一个)支持动画。|
79| edgeEffect<sup>10+</sup> | value:[EdgeEffect](ts-appendix-enums.md#edgeeffect), <br/>options?:[EdgeEffectOptions<sup>11+</sup>](ts-container-scroll.md#edgeeffectoptions11对象说明) | 设置边缘滑动效果。<br/>\- value: 设置Grid组件的边缘滑动效果,支持弹簧效果和阴影效果。<br/>默认值:EdgeEffect.None<br/>\- options:设置组件内容大小小于组件自身时,是否开启滑动效果。<br/>默认值:false|
80| enableScrollInteraction<sup>10+</sup>  |  boolean  |   设置是否支持滚动手势,当设置为false时,无法通过手指或者鼠标滚动,但不影响控制器的滚动接口。<br/>默认值:true      |
81| nestedScroll<sup>10+</sup>                 | [NestedScrollOptions](ts-container-scroll.md#nestedscrolloptions10对象说明)         | 嵌套滚动选项。设置向前向后两个方向上的嵌套滚动模式,实现与父组件的滚动联动。 |
82| friction<sup>10+</sup> | number \| [Resource](ts-types.md#resource)    | 设置摩擦系数,手动划动滚动区域时生效,只对惯性滚动过程有影响,对惯性滚动过程中的链式效果有间接影响。<br/>默认值:非可穿戴设备为0.6,可穿戴设备为0.9<br/>**说明:** <br/>设置为小于等于0的值时,按默认值处理 |
83
84Grid组件根据rowsTemplate、columnsTemplate属性的设置情况,可分为以下三种布局模式:
85
861、rowsTemplate、columnsTemplate同时设置:
87
88- Grid只展示固定行列数的元素,其余元素不展示,且Grid不可滚动。
89- 此模式下以下属性不生效:layoutDirection、maxCount、minCount、cellLength。
90- Grid的宽高没有设置时,默认适应父组件尺寸。
91- Grid网格列大小按照Grid自身内容区域大小减去所有行列Gap后按各个行列所占比重分配。
92- GridItem默认填满网格大小。
93
942、rowsTemplate、columnsTemplate仅设置其中的一个:
95
96- 元素按照设置的方向进行排布,超出Grid显示区域后,Grid可通过滚动的方式展示。
97- 如果设置了columnsTemplate,Grid滚动方向为垂直方向,主轴方向为垂直方向,交叉轴方向为水平方向。
98- 如果设置了rowsTemplate,Grid滚动方向为水平方向,主轴方向为水平方向,交叉轴方向为垂直方向。
99- 此模式下以下属性不生效:layoutDirection、maxCount、minCount、cellLength。
100- 网格交叉轴方向尺寸根据Grid自身内容区域交叉轴尺寸减去交叉轴方向所有Gap后按所占比重分配。
101- 网格主轴方向尺寸取当前网格交叉轴方向所有GridItem高度最大值。
102
1033、rowsTemplate、columnsTemplate都不设置:
104
105- 元素在layoutDirection方向上排布,列数由Grid的宽度、首个元素的宽度、minCount、maxCount、columnsGap共同决定。
106- 行数由Grid高度、首个元素高度、cellLength、rowsGap共同决定。超出行列容纳范围的元素不显示,也不能通过滚动进行展示。
107- 此模式下仅生效以下属性:layoutDirection、maxCount、minCount、cellLength、editMode、columnsGap、rowsGap。
108- 当前layoutDirection设置为Row时,先从左到右排列,排满一行再排下一行。剩余高度不足时不再布局,整体内容顶部居中。
109- 当前layoutDirection设置为Column时,先从上到下排列,排满一列再排下一列,剩余宽度不足时不再布局。整体内容顶部居中。
110
111### flingSpeedLimit<sup>11+</sup>
112
113flingSpeedLimit(speedLimit: number)
114
115限制跟手滑动结束后,Fling动效开始时的最大初始速度。单位是vp/s。
116
117**系统能力:** SystemCapability.ArkUI.ArkUI.Full
118
119**参数:**
120
121| 参数名     | 类型   | 必填 | 说明                            |
122| ---------- | ------ | ---- | ------------------------------- |
123| speedLimit | number | 是   | Fling动效开始时的最大初始速度。 |
124
125## GridDirection<sup>8+</sup>枚举说明
126
127| 名称   |枚举值| 描述                                   |
128| ------ |------| -------------------------------------- |
129| Row  |  0  | 主轴布局方向沿水平方向布局,即自左往右先填满一行,再去填下一行。 |
130| Column |  1  | 主轴布局方向沿垂直方向布局,即自上往下先填满一列,再去填下一列。 |
131| RowReverse    |  2  | 主轴布局方向沿水平方向反向布局,即自右往左先填满一行,再去填下一行。 |
132| ColumnReverse   |  3  | 主轴布局方向沿垂直方向反向布局,即自下往上先填满一列,再去填下一列。 |
133
134> **说明:**
135>
136> Grid组件[通用属性clip](ts-universal-attributes-sharp-clipping.md)的默认值为true。
137
138## 事件
139
140除支持[通用事件](ts-universal-events-click.md)外,还支持以下事件:
141
142| 名称 | 功能描述 |
143| -------- | -------- |
144| onScrollIndex(event: (first: number, last<sup>10+</sup>: number) => void) | 当前网格显示的起始位置/终止位置的item发生变化时触发。网格初始化时会触发一次。<br/>- first: 当前显示的网格起始位置的索引值。<br/>- last: 当前显示的网格终止位置的索引值。<br/>Grid显示区域上第一个子组件/最后一个组件的索引值有变化就会触发。 |
145| onItemDragStart<sup>8+</sup>(event: (event: ItemDragInfo, itemIndex: number) => (() => any) \| void) | 开始拖拽网格元素时触发。<br/>- event: 见[ItemDragInfo对象说明](#itemdraginfo对象说明)。<br/>- itemIndex: 被拖拽网格元素索引值。<br/>**说明:** <br/>返回void表示不能拖拽。<br/>手指长按GridItem时触发该事件。<br/>由于拖拽检测也需要长按,且事件处理机制优先触发子组件事件,GridItem上绑定LongPressGesture时无法触发拖拽;如有长按和拖拽同时使用的需求可以使用通用拖拽事件。 |
146| onItemDragEnter<sup>8+</sup>(event: (event: ItemDragInfo) => void) | 拖拽进入网格元素范围内时触发。<br/>- event: 见[ItemDragInfo对象说明](#itemdraginfo对象说明)。 |
147| onItemDragMove<sup>8+</sup>(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number) => void) | 拖拽在网格元素范围内移动时触发。<br/>- event: 见[ItemDragInfo对象说明](#itemdraginfo对象说明)。<br/>- itemIndex: 拖拽起始位置。<br/>- insertIndex: 拖拽插入位置。 |
148| onItemDragLeave<sup>8+</sup>(event: (event: ItemDragInfo, itemIndex: number) => void) | 拖拽离开网格元素时触发。<br/>- event: 见[ItemDragInfo对象说明](#itemdraginfo对象说明)。<br/>- itemIndex: 拖拽离开的网格元素索引值。 |
149| onItemDrop<sup>8+</sup>(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => void) | 绑定该事件的网格元素可作为拖拽释放目标,当在网格元素内停止拖拽时触发。<br/>- event: 见[ItemDragInfo对象说明](#itemdraginfo对象说明)。<br/>- itemIndex: 拖拽起始位置。<br/>- insertIndex: 拖拽插入位置。<br/>- isSuccess: 是否成功释放。 |
150| onScrollBarUpdate<sup>10+</sup>(event: (index: number, offset: number) => ComputedBarAttribute) | 当前网格显示的起始位置item发生变化时触发,可通过该回调设置滚动条的位置及长度。<br/>- index: 当前显示的网格起始位置的索引值。<br/>- offset: 当前显示的网格起始位置元素相对网格显示起始位置的偏移,单位vp。<br/>- ComputedBarAttribute: 见[ComputedBarAttribute对象说明](#computedbarattribute对象说明)。  |
151| onScroll<sup>10+</sup>(event: (scrollOffset: number, scrollState: [ScrollState](ts-container-list.md#scrollstate枚举说明)) => void) | 网格滑动时触发。<br/>- scrollOffset: 每帧滚动的偏移量,Grid的内容向上滚动时偏移量为正,向下滚动时偏移量为负,单位vp。<br/>- scrollState: 当前滑动状态。 |
152| onReachStart<sup>10+</sup>(event: () => void)          | 网格到达起始位置时触发。<br/>**说明:** <br>Grid初始化时会触发一次,Grid滚动到起始位置时触发一次。Grid边缘效果为弹簧效果时,划动经过起始位置时触发一次,回弹回起始位置时再触发一次。 |
153| onReachEnd<sup>10+</sup>(event: () => void)            | 网格到达末尾位置时触发。<br/>**说明:** <br/>Grid边缘效果为弹簧效果时,划动经过末尾位置时触发一次,回弹回末尾位置时再触发一次。 |
154| onScrollFrameBegin<sup>10+</sup>(event: (offset: number, state:  [ScrollState](ts-container-list.md#scrollstate枚举说明)) => { offsetRemain: number }) | 网格开始滑动时触发,事件参数传入即将发生的滑动量,事件处理函数中可根据应用场景计算实际需要的滑动量并作为事件处理函数的返回值返回,网格将按照返回值的实际滑动量进行滑动。<br/>\- offset:即将发生的滑动量,单位vp。<br/>\- state:当前滑动状态。<br/>- offsetRemain:实际滑动量,单位vp。<br/>触发该事件的条件:手指拖动Grid、Grid惯性划动时每帧开始时触发;Grid超出边缘回弹、使用滚动控制器和拖动滚动条的滚动不会触发。<br/>**说明:** <br/>当gridDirection的值为Axis.Vertical时,返回垂直方向滑动量,当gridDirection的值为Axis.Horizontal时,返回水平方向滑动量。 |
155| onScrollStart<sup>10+</sup>(event: () => void) | 网格滑动开始时触发。手指拖动网格或网格的滚动条触发的滑动开始时,会触发该事件。使用[Scroller](ts-container-scroll.md#scroller)滑动控制器触发的带动画的滑动,动画开始时会触发该事件。 |
156| onScrollStop<sup>10+</sup>(event: () => void)          | 网格滑动停止时触发。手指拖动网格或网格的滚动条触发的滑动,手指离开屏幕并且滑动停止时会触发该事件;使用[Scroller](ts-container-scroll.md#scroller)滑动控制器触发的带动画的滑动,动画停止会触发该事件。 |
157
158## ItemDragInfo对象说明
159
160| 名称         | 类型         |   描述         |
161| ---------- | ---------- | ---------- |
162| x | number |  当前拖拽点的x坐标,单位vp。    |
163| y   | number |  当前拖拽点的y坐标,单位vp。    |
164
165## ComputedBarAttribute对象说明
166
167| 名称         | 类型         |   描述         |
168| ---------- | ---------- | ---------- |
169| totalOffset | number |  Grid内容相对显示区域的总偏移,单位px。    |
170| totalLength   | number |  Grid内容总长度,单位px。    |
171
172## 示例
173
174### 示例1
175
176固定行列的Grid,可以使用GridLayoutOptions中的onGetRectByIndex指定GridItem的位置和大小。
177
178```ts
179// xxx.ets
180@Entry
181@Component
182struct GridExample {
183  @State numbers1: String[] = ['0', '1', '2', '3', '4']
184  @State numbers2: String[] = ['0', '1','2','3','4','5']
185
186  layoutOptions3: GridLayoutOptions = {
187    regularSize: [1, 1],
188    onGetRectByIndex: (index: number) => {
189      if (index == 0)
190        return [0, 0, 1, 1]
191      else if(index==1)
192        return [0, 1, 2, 2]
193      else if(index==2)
194        return [0 ,3 ,3 ,3]
195      else if(index==3)
196        return [3, 0, 3, 3]
197      else if(index==4)
198        return [4, 3, 2, 2]
199      else
200        return [5, 5, 1, 1]
201    }
202  }
203
204  build() {
205    Column({ space: 5 }) {
206      Grid() {
207        ForEach(this.numbers1, (day: string) => {
208          ForEach(this.numbers1, (day: string) => {
209            GridItem() {
210              Text(day)
211                .fontSize(16)
212                .backgroundColor(0xF9CF93)
213                .width('100%')
214                .height('100%')
215                .textAlign(TextAlign.Center)
216            }
217          }, (day: string) => day)
218        }, (day: string) => day)
219      }
220      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
221      .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
222      .columnsGap(10)
223      .rowsGap(10)
224      .width('90%')
225      .backgroundColor(0xFAEEE0)
226      .height(300)
227
228      Text('GridLayoutOptions的使用:onGetRectByIndex。').fontColor(0xCCCCCC).fontSize(9).width('90%')
229
230      Grid(undefined, this.layoutOptions3) {
231        ForEach(this.numbers2, (day: string) => {
232          GridItem() {
233            Text(day)
234              .fontSize(16)
235              .backgroundColor(0xF9CF93)
236              .width('100%')
237              .height("100%")
238              .textAlign(TextAlign.Center)
239          }
240          .height("100%")
241          .width('100%')
242        }, (day: string) => day)
243      }
244      .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
245      .rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
246      .columnsGap(10)
247      .rowsGap(10)
248      .width('90%')
249      .backgroundColor(0xFAEEE0)
250      .height(300)
251    }.width('100%').margin({ top: 5 })
252  }
253}
254```
255
256![zh-cn_image_0000001219744183](figures/zh-cn_image_0000001219744183.gif)
257
258### 示例2
259
260可滚动Grid,包括所有滚动属性和事件。
261
262```ts
263// xxx.ets
264@Entry
265@Component
266struct GridExample {
267  @State numbers: String[] = ['0', '1', '2', '3', '4']
268  scroller: Scroller = new Scroller()
269  @State Position: number = 0 //0代表滚动到grid顶部,1代表中间值,2代表滚动到grid底部。
270
271  build() {
272    Column({ space: 5 }) {
273      Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')
274      Grid(this.scroller) {
275        ForEach(this.numbers, (day: string) => {
276          ForEach(this.numbers, (day: string) => {
277            GridItem() {
278              Text(day)
279                .fontSize(16)
280                .backgroundColor(0xF9CF93)
281                .width('100%')
282                .height(80)
283                .textAlign(TextAlign.Center)
284            }
285          }, (day: string) => day)
286        }, (day: string) => day)
287      }
288      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
289      .columnsGap(10)
290      .rowsGap(10)
291      .friction(0.6)
292      .enableScrollInteraction(true)
293      .supportAnimation(false)
294      .multiSelectable(false)
295      .edgeEffect(EdgeEffect.Spring)
296      .scrollBar(BarState.On)
297      .scrollBarColor(Color.Grey)
298      .scrollBarWidth(4)
299      .width('90%')
300      .backgroundColor(0xFAEEE0)
301      .height(300)
302      .onScrollIndex((first: number, last: number) => {
303        console.info(first.toString())
304        console.info(last.toString())
305      })
306      .onScrollBarUpdate((index: number, offset: number) => {
307        console.info("XXX" + 'Grid onScrollBarUpdate,index : ' + index.toString() + ",offset" + offset.toString())
308        return { totalOffset: (index / 5) * (80 + 10) - offset, totalLength: 80 * 5 + 10 * 4 }
309      })  //只适用于当前示例代码数据源,如果数据源有变化,则需要修改该部分代码,或者删掉此属性
310      .onScroll((scrollOffset: number, scrollState: ScrollState) => {
311        console.info(scrollOffset.toString())
312        console.info(scrollState.toString())
313      })
314      .onScrollStart(() => {
315        console.info("XXX" + "Grid onScrollStart")
316      })
317      .onScrollStop(() => {
318        console.info("XXX" + "Grid onScrollStop")
319      })
320      .onReachStart(() => {
321        this.Position = 0
322        console.info("XXX" + "Grid onReachStart")
323      })
324      .onReachEnd(() => {
325        this.Position = 2
326        console.info("XXX" + "Grid onReachEnd")
327      })
328
329      Button('next page')
330        .onClick(() => { // 点击后滑到下一页
331          this.scroller.scrollPage({ next: true })
332        })
333    }.width('100%').margin({ top: 5 })
334  }
335}
336```
337
338![scrollerExample2](figures/scrollerExample2.gif)
339
340### 示例3
341
342GridLayoutOptions的使用:irregularIndexes与onGetIrregularSizeByIndex。
343
344```ts
345// xxx.ets
346@Entry
347@Component
348struct GridExample {
349  @State numbers: String[] = ['0', '1', '2', '3', '4']
350  scroller: Scroller = new Scroller()
351  layoutOptions1: GridLayoutOptions = {
352    regularSize: [1, 1],        // 只支持[1, 1]
353    irregularIndexes: [0, 6],   // 索引为0和6的GridItem占用一行
354  }
355
356  layoutOptions2: GridLayoutOptions = {
357    regularSize: [1, 1],
358    irregularIndexes: [0, 7],   // 索引为0和7的GridItem占用的列数由onGetIrregularSizeByIndex指定
359    onGetIrregularSizeByIndex: (index: number) => {
360      if (index === 0) {
361        return [1, 5]
362      }
363      return [1, index % 6 + 1]
364    }
365  }
366
367  build() {
368    Column({ space: 5 }) {
369      Grid(this.scroller, this.layoutOptions1) {
370        ForEach(this.numbers, (day: string) => {
371          ForEach(this.numbers, (day: string) => {
372            GridItem() {
373              Text(day)
374                .fontSize(16)
375                .backgroundColor(0xF9CF93)
376                .width('100%')
377                .height(80)
378                .textAlign(TextAlign.Center)
379            }.selectable(false)
380          }, (day: string) => day)
381        }, (day: string) => day)
382      }
383      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
384      .columnsGap(10)
385      .rowsGap(10)
386      .multiSelectable(true)
387      .scrollBar(BarState.Off)
388      .width('90%')
389      .backgroundColor(0xFAEEE0)
390      .height(300)
391
392      Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')
393      // 不使用scroll,需要undefined占位
394      Grid(undefined, this.layoutOptions2) {
395        ForEach(this.numbers, (day: string) => {
396          ForEach(this.numbers, (day: string) => {
397            GridItem() {
398              Text(day)
399                .fontSize(16)
400                .backgroundColor(0xF9CF93)
401                .width('100%')
402                .height(80)
403                .textAlign(TextAlign.Center)
404            }
405          }, (day: string) => day)
406        }, (day: string) => day)
407      }
408      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
409      .columnsGap(10)
410      .rowsGap(10)
411      .scrollBar(BarState.Off)
412      .width('90%')
413      .backgroundColor(0xFAEEE0)
414      .height(300)
415    }.width('100%').margin({ top: 5 })
416  }
417}
418```
419
420![gridLayoutOptions](figures/gridLayoutOptions.gif)
421
422### 示例4
423
424nestedScroll和onScrollFrameBegin的使用。
425
426```ts
427@Entry
428@Component
429struct GridExample {
430  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
431  @State numbers: number[] = []
432  @State translateY: number = 0
433  private scroller: Scroller = new Scroller()
434  private gridScroller: Scroller = new Scroller()
435  private touchDown: boolean = false
436  private listTouchDown: boolean = false
437  private scrolling: boolean = false
438
439  aboutToAppear() {
440    for (let i = 0; i < 100; i++) {
441      this.numbers.push(i)
442    }
443  }
444
445  build() {
446    Stack() {
447      Column() {
448        Row() {
449          Text('Head')
450        }
451
452        Column() {
453          List({ scroller: this.scroller }) {
454            ListItem() {
455              Grid() {
456                GridItem() {
457                  Text('GoodsTypeList1')
458                }
459                .backgroundColor(this.colors[0])
460                .columnStart(0)
461                .columnEnd(1)
462
463                GridItem() {
464                  Text('GoodsTypeList2')
465                }
466                .backgroundColor(this.colors[1])
467                .columnStart(0)
468                .columnEnd(1)
469
470                GridItem() {
471                  Text('GoodsTypeList3')
472                }
473                .backgroundColor(this.colors[2])
474                .columnStart(0)
475                .columnEnd(1)
476
477                GridItem() {
478                  Text('GoodsTypeList4')
479                }
480                .backgroundColor(this.colors[3])
481                .columnStart(0)
482                .columnEnd(1)
483
484                GridItem() {
485                  Text('GoodsTypeList5')
486                }
487                .backgroundColor(this.colors[4])
488                .columnStart(0)
489                .columnEnd(1)
490              }
491              .scrollBar(BarState.Off)
492              .columnsGap(15)
493              .rowsGap(10)
494              .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
495              .columnsTemplate('1fr')
496              .width('100%')
497              .height(200)
498            }
499
500            ListItem() {
501              Grid(this.gridScroller) {
502                ForEach(this.numbers, (item: number) => {
503                  GridItem() {
504                    Text(item + '')
505                      .fontSize(16)
506                      .backgroundColor(0xF9CF93)
507                      .width('100%')
508                      .height('100%')
509                      .textAlign(TextAlign.Center)
510                  }
511                  .width('100%')
512                  .height(40)
513                  .shadow({ radius: 10, color: '#909399', offsetX: 1, offsetY: 1 })
514                  .borderRadius(10)
515                  .translate({ x: 0, y: this.translateY })
516                }, (item: string) => item)
517              }
518              .columnsTemplate('1fr 1fr')
519              .friction(0.3)
520              .columnsGap(15)
521              .rowsGap(10)
522              .scrollBar(BarState.Off)
523              .width('100%')
524              .height('100%')
525              .layoutDirection(GridDirection.Column)
526              .nestedScroll({
527                scrollForward: NestedScrollMode.PARENT_FIRST,
528                scrollBackward: NestedScrollMode.SELF_FIRST
529              })
530              .onTouch((event: TouchEvent) => {
531                if (event.type == TouchType.Down) {
532                  this.listTouchDown = true
533                } else if (event.type == TouchType.Up) {
534                  this.listTouchDown = false
535                }
536              })
537            }
538          }
539          .scrollBar(BarState.Off)
540          .edgeEffect(EdgeEffect.None)
541          .onTouch((event: TouchEvent) => {
542            if (event.type == TouchType.Down) {
543              this.touchDown = true
544            } else if (event.type == TouchType.Up) {
545              this.touchDown = false
546            }
547          })
548          .onScrollFrameBegin((offset: number, state: ScrollState) => {
549            if (this.scrolling && offset > 0) {
550              let newOffset = this.scroller.currentOffset().yOffset
551              if (newOffset >= 590) {
552                this.gridScroller.scrollBy(0, offset)
553                return { offsetRemain: 0 }
554              } else if (newOffset + offset > 590) {
555                this.gridScroller.scrollBy(0, newOffset + offset - 590)
556                return { offsetRemain: 590 - newOffset }
557              }
558            }
559            return { offsetRemain: offset }
560          })
561          .onScrollStart(() => {
562            if (this.touchDown && !this.listTouchDown) {
563              this.scrolling = true
564            }
565          })
566          .onScrollStop(() => {
567            this.scrolling = false
568          })
569        }
570        .width('100%')
571        .height('100%')
572        .padding({ left: 10, right: 10 })
573      }
574
575      Row() {
576        Text('Top')
577          .width(30)
578          .height(30)
579          .borderRadius(50)
580      }
581      .padding(5)
582      .borderRadius(50)
583      .backgroundColor('#ffffff')
584      .shadow({ radius: 10, color: '#909399', offsetX: 1, offsetY: 1 })
585      .margin({ right: 22, bottom: 15 })
586      .onClick(() => {
587        this.scroller.scrollTo({ xOffset: 0, yOffset: 0 })
588        this.gridScroller.scrollTo({ xOffset: 0, yOffset: 0 })
589      })
590    }
591    .align(Alignment.BottomEnd)
592  }
593}
594```
595
596![nestedScrollExample4](figures/nestedScrollExample4.gif)
597
598### 示例5
599
6001.  设置属性editMode\(true\)设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem。
6012.  在[onItemDragStart](#事件)回调中设置拖拽过程中显示的图片。
6023.  在[onItemDrop](#事件)中获取拖拽起始位置,和拖拽插入位置,并在[onItemDrop](#事件)中完成交换数组位置逻辑。
603
604> **说明:**
605>
606> 预览器窗口不支持显示拖拽跟手。
607
608```ts
609@Entry
610@Component
611struct GridExample {
612  @State numbers: string[] = []
613  scroller: Scroller = new Scroller()
614  @State text: string = 'drag'
615
616  @Builder pixelMapBuilder() { //拖拽过程样式
617    Column() {
618      Text(this.text)
619        .fontSize(16)
620        .backgroundColor(0xF9CF93)
621        .width(80)
622        .height(80)
623        .textAlign(TextAlign.Center)
624    }
625  }
626
627  aboutToAppear() {
628    for (let i = 1;i <= 15; i++) {
629      this.numbers.push(i + '')
630    }
631  }
632
633  changeIndex(index1: number, index2: number) { //交换数组位置
634    let temp: string;
635    temp = this.numbers[index1];
636    this.numbers[index1] = this.numbers[index2];
637    this.numbers[index2] = temp;
638  }
639
640  build() {
641    Column({ space: 5 }) {
642      Grid(this.scroller) {
643        ForEach(this.numbers, (day: string) => {
644          GridItem() {
645            Text(day)
646              .fontSize(16)
647              .backgroundColor(0xF9CF93)
648              .width(80)
649              .height(80)
650              .textAlign(TextAlign.Center)
651          }
652        })
653      }
654      .columnsTemplate('1fr 1fr 1fr')
655      .columnsGap(10)
656      .rowsGap(10)
657      .width('90%')
658      .backgroundColor(0xFAEEE0)
659      .height(300)
660      .editMode(true) //设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem
661      .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { //第一次拖拽此事件绑定的组件时,触发回调。
662        this.text = this.numbers[itemIndex]
663        return this.pixelMapBuilder() //设置拖拽过程中显示的图片。
664      })
665      .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { //绑定此事件的组件可作为拖拽释放目标,当在本组件范围内停止拖拽行为时,触发回调。
666        // isSuccess=false时,说明drop的位置在grid外部;insertIndex > length时,说明有新增元素的事件发生
667        if (!isSuccess || insertIndex >= this.numbers.length) {
668          return
669        }
670        console.info('beixiang' + itemIndex + '', insertIndex + '') //itemIndex拖拽起始位置,insertIndex拖拽插入位置
671        this.changeIndex(itemIndex, insertIndex)
672      })
673    }.width('100%').margin({ top: 5 })
674  }
675}
676```
677
678示例图:
679
680网格子组件开始拖拽:
681
682![gridDrag](figures/gridDrag.png)
683
684网格子组件拖拽过程中:
685
686![gridDrag](figures/gridDrag1.png)
687
688网格子组件1与子组件6拖拽交换位置后:
689
690![gridDrag](figures/gridDrag2.png)
691
692### 示例6
693
694layoutDirection、maxcount、minCount、cellLength的使用。
695
696```ts
697@Entry
698@Component
699struct GridExample {
700  @State numbers: string[] = []
701
702  aboutToAppear() {
703    for (let i = 1; i <= 30; i++) {
704      this.numbers.push(i + '')
705    }
706  }
707
708  build() {
709    Scroll() {
710      Column({ space: 5 }) {
711        Blank()
712        Text('rowsTemplate、columnsTemplate都不设置layoutDirection、maxcount、minCount、cellLength才生效')
713          .fontSize(15).fontColor(0xCCCCCC).width('90%')
714        Grid() {
715          ForEach(this.numbers, (day: string) => {
716            GridItem() {
717              Text(day).fontSize(16).backgroundColor(0xF9CF93)
718            }.width(40).height(80).borderWidth(2).borderColor(Color.Red)
719          }, (day: string) => day)
720        }
721        .height(300)
722        .columnsGap(10)
723        .rowsGap(10)
724        .backgroundColor(0xFAEEE0)
725        .maxCount(6)
726        .minCount(2)
727        .cellLength(0)
728        .layoutDirection(GridDirection.Row)
729      }
730      .width('90%').margin({ top: 5, left: 5, right: 5 })
731      .align(Alignment.Center)
732    }
733  }
734}
735```
736
737![cellLength](figures/cellLength.gif)
738