• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Grid
2
3<!--Kit: ArkUI-->
4<!--Subsystem: ArkUI-->
5<!--Owner: @zcdqs; @fangyuhao-->
6<!--Designer: @zcdqs-->
7<!--Tester: @liuzhenshuo-->
8<!--Adviser: @HelloCrease-->
9
10网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
11
12>  **说明:**
13>
14>  该组件从API version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
15>
16>  组件内部已绑定手势实现跟手滚动等功能,需要增加自定义手势操作时请参考[手势拦截增强](ts-gesture-blocking-enhancement.md)进行处理。
17
18
19## 子组件
20
21仅支持[GridItem](ts-container-griditem.md)子组件和自定义组件。自定义组件在Grid下使用时,建议使用GridItem作为自定组件的顶层组件,不建议给自定义组件设置属性和事件方法。
22支持通过渲染控制类型([if/else](../../../ui/state-management/arkts-rendering-control-ifelse.md)、[ForEach](../../../ui/state-management/arkts-rendering-control-foreach.md)、[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)和[Repeat](../../../ui/state-management/arkts-new-rendering-control-repeat.md))动态生成子组件,更推荐使用LazyForEach或Repeat以优化性能。
23
24>  **说明:**
25>
26>  Grid子组件的索引值计算规则:
27>
28>  按子组件的顺序依次递增。
29>
30>  if/else语句中,只有条件成立分支内的子组件会参与索引值计算,条件不成立分支内的子组件不计算索引值。
31>
32>  ForEach/LazyForEach和Repeat语句中,会计算展开所有子组件索引值。
33>
34>  [if/else](../../../ui/state-management/arkts-rendering-control-ifelse.md)、[ForEach](../../../ui/state-management/arkts-rendering-control-foreach.md)、[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)和[Repeat](../../../ui/state-management/arkts-new-rendering-control-repeat.md)发生变化以后,会更新子组件索引值。
35>
36>  Grid子组件的visibility属性设置为Hidden或None时依然会计算索引值。
37>
38>  Grid子组件的visibility属性设置为None时不显示,但依然会占用子组件对应的网格。
39>
40>  Grid子组件设置position属性,会占用子组件对应的网格,子组件将显示在相对Grid左上角偏移position的位置。该子组件不会随其对应网格滚动,在对应网格滑出Grid显示范围外后不显示。
41>
42>  当Grid子组件之间留有空隙时,会根据当前的展示区域尽可能填补空隙,因此GridItem可能会随着网格滚动而改变相对位置。
43
44## 接口
45
46Grid(scroller?: Scroller, layoutOptions?: GridLayoutOptions)
47
48创建网格容器。
49
50**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
51
52**系统能力:** SystemCapability.ArkUI.ArkUI.Full
53
54**参数:**
55
56| 参数名   | 类型                                    | 必填 | 说明                                                     |
57| -------- | ------------------------------------------- | ---- | ------------------------------------------------------------ |
58| scroller | [Scroller](ts-container-scroll.md#scroller) | 否   | 可滚动组件的控制器。用于与可滚动组件进行绑定。<br/>**说明:** <br/>不允许和其他滚动类组件,如:[ArcList](ts-container-arclist.md)、[List](ts-container-list.md)、[Grid](ts-container-grid.md)、[Scroll](ts-container-scroll.md)和[WaterFlow](ts-container-waterflow.md)绑定同一个滚动控制对象。 |
59| layoutOptions<sup>10+</sup> | [GridLayoutOptions](#gridlayoutoptions10对象说明) | 否 | Grid布局选项。 |
60
61## GridLayoutOptions<sup>10+</sup>对象说明
62
63Grid布局选项。其中,irregularIndexes和onGetIrregularSizeByIndex可对仅设置rowsTemplate或columnsTemplate的Grid使用,可以指定一个index数组,并为其中的index对应的GridItem设置其占据的行数与列数,使用方法参见[示例3](#示例3可滚动grid设置跨行跨列节点);onGetRectByIndex可对同时设置rowsTemplate和columnsTemplate的Grid使用,为指定的index对应的GridItem设置位置和大小,使用方法参见[示例1](#示例1固定行列grid)。
64
65为提高Grid在跳转、列数变化等场景的性能,应该尽量使用GridLayoutOptions。即使Grid中没有任何特殊的跨行跨列节点,也可以通过使用'Grid(this.scroller, {regularSize: [1, 1]})'的方式提高跳转性能。参考<!--RP1-->[使用GridLayoutOptions提升Grid性能](../../../performance/grid_optimization.md#使用gridlayoutoptions提升grid性能)<!--RP1End-->。
66
67**系统能力:** SystemCapability.ArkUI.ArkUI.Full
68
69| 名称    | 类型      | 只读   | 可选 | 说明                    |
70| ----- | ------- | ---- | --  | --------------------- |
71| regularSize  | [number, number]  | 否    | 否 | 大小规则的GridItem在Grid中占的行数和列数,只支持占1行1列即[1, 1]。 <br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。  |
72| irregularIndexes | number[] | 否    | 是 | 指定索引的GridItem在Grid中的大小是不规则的。当不设置onGetIrregularSizeByIndex时,irregularIndexes中GridItem的默认大小为垂直滚动Grid的一整行或水平滚动Grid的一整列。 <br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 |
73| onGetIrregularSizeByIndex | (index: number) => [number, number] | 否    | 是 | 配合irregularIndexes使用,设置不规则GridItem占用的行数和列数。开发者可为irregularIndexes中指明的index对应的GridItem设置占用的行数和列数。在API version 12之前,垂直滚动Grid不支持GridItem占多行,水平滚动Grid不支持GridItem占多列。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 |
74| 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大小,则只会布局一部分。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 |
75
76## 属性
77
78除支持[通用属性](ts-component-general-attributes.md)和[滚动组件通用属性](ts-container-scrollable-common.md#属性)外,还支持以下属性:
79> **说明:**
80>
81> Grid组件使用通用属性[clip<sup>12+</sup>](ts-universal-attributes-sharp-clipping.md#clip12)和通用属性[clip<sup>18+</sup>](ts-universal-attributes-sharp-clipping.md#clip18)时默认值都为true。
82>
83> 设置Grid的padding后,如果子组件部分位于Grid内容区且部分位于padding区域内,则会显示;如果子组件完全位于padding区域内,则不会显示。如下图所示,GridItem1显示,而GridItem2不显示。
84>
85> ![GridPadding示意图](figures/gridPadding.png)
86
87### columnsTemplate
88
89columnsTemplate(value: string)
90
91设置当前网格布局列的数量、固定列宽或最小列宽值,不设置时默认1列。
92
93例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'&nbsp;是将父组件分3列,将父组件允许的宽分为4等份,第1列占1份,第2列占1份,第3列占2份。
94
95columnsTemplate('repeat(auto-fit, track-size)')是设置最小列宽值为track-size,自动计算列数和实际列宽。
96
97columnsTemplate('repeat(auto-fill, track-size)')是设置固定列宽值为track-size,自动计算列数。
98
99columnsTemplate('repeat(auto-stretch, track-size)')是设置固定列宽值为track-size,使用columnsGap为最小列间距,自动计算列数和实际列间距。
100
101其中repeat、auto-fit、auto-fill、auto-stretch为关键字。track-size为列宽,支持的单位包括px、vp、%或有效数字,默认单位为vp,track-size至少包括一个有效列宽。<br/>
102auto-stretch模式只支持track-size为一个有效列宽值,并且track-size只支持px、vp和有效数字,不支持%。
103
104使用效果可以参考[示例8](#示例8设置自适应列数)。
105
106设置为'0fr'时,该列的列宽为0,不显示GridItem。设置为其他非法值时,GridItem显示为固定1列。
107
108**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
109
110**系统能力:** SystemCapability.ArkUI.ArkUI.Full
111
112**参数:**
113
114| 参数名 | 类型   | 必填 | 说明                               |
115| ------ | ------ | ---- | ---------------------------------- |
116| value  | string | 是   | 当前网格布局列的数量或最小列宽值。 |
117
118### rowsTemplate
119
120rowsTemplate(value: string)
121
122设置当前网格布局行的数量、固定行高或最小行高值,不设置时默认1行。
123
124例如,&nbsp;'1fr&nbsp;1fr&nbsp;2fr'是将父组件分3行,将父组件允许的高分为4等份,第1行占1份,第2行占1份,第3行占2份。
125
126rowsTemplate('repeat(auto-fit, track-size)')是设置最小行高值为track-size,自动计算行数和实际行高。
127
128rowsTemplate('repeat(auto-fill, track-size)')是设置固定行高值为track-size,自动计算行数。
129
130rowsTemplate('repeat(auto-stretch, track-size)')是设置固定行高值为track-size,使用rowsGap为最小行间距,自动计算行数和实际行间距。
131
132其中repeat、auto-fit、auto-fill、auto-stretch为关键字。track-size为行高,支持的单位包括px、vp、%或有效数字,默认单位为vp,track-size至少包括一个有效行高。<br/>
133auto-stretch模式只支持track-size为一个有效行高值,并且track-size只支持px、vp和有效数字,不支持%。
134
135设置为'0fr',则这一行的行高为0,这一行GridItem不显示。设置为其他非法值,按固定1行处理。
136
137**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
138
139**系统能力:** SystemCapability.ArkUI.ArkUI.Full
140
141**参数:**
142
143| 参数名 | 类型   | 必填 | 说明                               |
144| ------ | ------ | ---- | ---------------------------------- |
145| value  | string | 是   | 当前网格布局行的数量或最小行高值。 |
146
147>  **说明:**
148>
149>  Grid组件根据rowsTemplate、columnsTemplate属性的设置情况,可分为以下三种布局模式:
150>
151>  1、rowsTemplate、columnsTemplate同时设置:
152>
153>  - Grid只展示固定行列数的元素,其余元素不展示,且Grid不可滚动。
154>  - 此模式下以下属性不生效:layoutDirection、maxCount、minCount、cellLength。
155>  - Grid的宽高没有设置时,默认适应父组件尺寸。
156>  - Grid网格列大小按照Grid自身内容区域大小减去所有行列Gap后按各个行列所占比重分配。
157>  - GridItem默认填满网格大小。
158>
159>  2、rowsTemplate、columnsTemplate仅设置其中的一个:
160>
161>  - 元素按照设置的方向进行排布,超出Grid显示区域后,Grid可通过滚动的方式展示。
162>  - 如果设置了columnsTemplate,Grid滚动方向为垂直方向,主轴方向为垂直方向,交叉轴方向为水平方向。
163>  - 如果设置了rowsTemplate,Grid滚动方向为水平方向,主轴方向为水平方向,交叉轴方向为垂直方向。
164>  - 此模式下以下属性不生效:layoutDirection、maxCount、minCount、cellLength。
165>  - 网格交叉轴方向尺寸根据Grid自身内容区域交叉轴尺寸减去交叉轴方向所有Gap后按所占比重分配。
166>  - 网格主轴方向尺寸取当前网格交叉轴方向所有GridItem主轴方向尺寸最大值。
167>
168>  3、rowsTemplate、columnsTemplate都不设置:
169>
170>  - 元素在layoutDirection方向上排布,列数由Grid的宽度、首个元素的宽度、minCount、maxCount、columnsGap共同决定。
171>  - 行数由Grid高度、首个元素高度、cellLength、rowsGap共同决定。超出行列容纳范围的元素不显示,也不能通过滚动进行展示。
172>  - 此模式下仅生效以下属性:layoutDirection、maxCount、minCount、cellLength、editMode、columnsGap、rowsGap。
173>  - 当前layoutDirection设置为Row时,先从左到右排列,排满一行再排下一行。剩余高度不足时不再布局,整体内容顶部居中。
174>  - 当前layoutDirection设置为Column时,先从上到下排列,排满一列再排下一列,剩余宽度不足时不再布局。整体内容顶部居中。
175>  - 当前Grid下面没有GridItem时,Grid的宽高为0。
176>
177
178### columnsGap
179
180columnsGap(value: Length)
181
182设置列与列的间距。设置为小于0的值时,按默认值显示。
183
184**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
185
186**系统能力:** SystemCapability.ArkUI.ArkUI.Full
187
188**参数:**
189
190| 参数名 | 类型                         | 必填 | 说明                         |
191| ------ | ---------------------------- | ---- | ---------------------------- |
192| value  | [Length](ts-types.md#length) | 是   | 列与列的间距。<br/>默认值:0 <br/>取值范围:[0, +∞) |
193
194### rowsGap
195
196rowsGap(value: Length)
197
198设置行与行的间距。设置为小于0的值时,按默认值显示。
199
200**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
201
202**系统能力:** SystemCapability.ArkUI.ArkUI.Full
203
204**参数:**
205
206| 参数名 | 类型                         | 必填 | 说明                         |
207| ------ | ---------------------------- | ---- | ---------------------------- |
208| value  | [Length](ts-types.md#length) | 是   | 行与行的间距。<br/>默认值:0 <br/>取值范围:[0, +∞) |
209
210### scrollBar
211
212scrollBar(value: BarState)
213
214设置滚动条状态。
215
216**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
217
218**系统能力:** SystemCapability.ArkUI.ArkUI.Full
219
220**参数:**
221
222| 参数名 | 类型                                      | 必填 | 说明                                                         |
223| ------ | ----------------------------------------- | ---- | ------------------------------------------------------------ |
224| value  | [BarState](ts-appendix-enums.md#barstate) | 是   | 滚动条状态。<br/>默认值:BarState.Auto<br/>**说明:** <br/>API version 9及以下版本默认值为BarState.Off,API version 10及以上版本的默认值为BarState.Auto。 |
225
226### scrollBarColor
227
228scrollBarColor(value: Color | number | string)
229
230设置滚动条的颜色。
231
232**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
233
234**系统能力:** SystemCapability.ArkUI.ArkUI.Full
235
236**参数:**
237
238| 参数名 | 类型                                                         | 必填 | 说明           |
239| ------ | ------------------------------------------------------------ | ---- | -------------- |
240| value  | [Color](ts-appendix-enums.md#color)&nbsp;\|&nbsp;number&nbsp;\|&nbsp;string | 是   | 滚动条的颜色。<br/>默认值:'\#182431'(40%不透明度)<br/>number为HEX格式颜色,支持rgb或者argb,示例:0xffffff。string为rgb或者argb格式颜色,示例:'#ffffff'。 |
241
242### scrollBarWidth
243
244scrollBarWidth(value: number | string)
245
246设置滚动条的宽度,不支持百分比设置。宽度设置后,滚动条正常状态和按压状态宽度均为滚动条的宽度值。如果滚动条的宽度超过Grid组件主轴方向的高度,则滚动条的宽度会变为默认值。
247
248**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
249
250**系统能力:** SystemCapability.ArkUI.ArkUI.Full
251
252**参数:**
253
254| 参数名 | 类型                       | 必填 | 说明                                      |
255| ------ | -------------------------- | ---- | ----------------------------------------- |
256| value  | number&nbsp;\|&nbsp;string | 是   | 滚动条的宽度。<br/>默认值:4<br/>单位:vp<br/>取值范围:设置为小于0的值时,按默认值处理。设置为0时,不显示滚动条。 |
257
258### cachedCount
259
260cachedCount(value: number)
261
262设置预加载的GridItem的数量,只在[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)和开启了virtualScroll开关的[Repeat](../../../ui/state-management/arkts-new-rendering-control-repeat.md)中生效。<!--Del-->具体使用可参考[减少应用白块说明](../../../performance/arkts-performance-improvement-recommendation.md#减少应用滑动白块)。<!--DelEnd-->
263
264设置缓存后会在Grid显示区域上下各缓存cachedCount*列数个GridItem。
265
266[LazyForEach](../../../ui/state-management/arkts-rendering-control-lazyforeach.md)和开启了virtualScroll开关的[Repeat](../../../ui/state-management/arkts-new-rendering-control-repeat.md)超出显示和缓存范围的GridItem会被释放。
267
268**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
269
270**系统能力:** SystemCapability.ArkUI.ArkUI.Full
271
272**参数:**
273
274| 参数名 | 类型   | 必填 | 说明                                                         |
275| ------ | ------ | ---- | ------------------------------------------------------------ |
276| value  | number | 是   | 预加载的GridItem的数量。<br/>默认值:垂直滚动时为一个屏幕内可显示的行数,水平滚动时为一个屏幕内可显示的列数,最大值为16。<br/>取值范围:[0, +∞),设置为小于0的值时,按1处理。<br/>通过状态变量单独更新value值时,Grid组件不会触发布局更新,缓存节点数量仅会在下次布局时更新。 |
277
278
279### cachedCount<sup>14+</sup>
280
281cachedCount(count: number, show: boolean)
282
283设置预加载的GridItem数量,并配置是否显示预加载节点。
284
285设置缓存后会在Grid显示区域上下各缓存cachedCount*列数个GridItem。配合[裁剪](ts-universal-attributes-sharp-clipping.md#clip12)或[内容裁剪](ts-container-scrollable-common.md#clipcontent14)属性可以显示出预加载节点。
286
287**原子化服务API:** 从API version 14开始,该接口支持在原子化服务中使用。
288
289**系统能力:** SystemCapability.ArkUI.ArkUI.Full
290
291**参数:**
292
293| 参数名 | 类型   | 必填 | 说明                                   |
294| ------ | ------ | ---- | -------------------------------------- |
295| count  | number | 是   | 预加载的GridItem的数量。<br/>默认值:垂直滚动时为一个屏幕内可显示的行数,水平滚动时为一个屏幕内可显示的列数,最大值为16。<br/>取值范围:[0, +∞),设置为小于0的值时,按1处理。<br/>通过状态变量单独更新count值时,Grid组件不会触发布局更新,缓存节点数量仅会在下次布局时更新。 |
296| show  | boolean | 是   | 被预加载的GridItem是否需要显示。设置为true时显示预加载的GridItem,设置为false时不显示预加载的GridItem。 <br/> 默认值:false |
297
298
299### editMode<sup>8+</sup>
300
301editMode(value: boolean)
302
303设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部[GridItem](ts-container-griditem.md)。
304
305**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
306
307**系统能力:** SystemCapability.ArkUI.ArkUI.Full
308
309**参数:**
310
311| 参数名 | 类型   | 必填 | 说明                                     |
312| ------ | ------ | ---- | ---------------------------------------- |
313| value  | boolean | 是   | Grid是否进入编辑模式。设置为true时当前Grid组件处于可编辑模式,设置为false时当前Grid组件处于不可编辑模式。<br/>默认值:false |
314
315### layoutDirection<sup>8+</sup>
316
317layoutDirection(value: GridDirection)
318
319设置布局的主轴方向。
320
321**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
322
323**系统能力:** SystemCapability.ArkUI.ArkUI.Full
324
325**参数:**
326
327| 参数名 | 类型                                     | 必填 | 说明                                           |
328| ------ | ---------------------------------------- | ---- | ---------------------------------------------- |
329| value  | [GridDirection](#griddirection8枚举说明) | 是   | 布局的主轴方向。<br/>默认值:GridDirection.Row |
330
331### maxCount<sup>8+</sup>
332
333maxCount(value: number)
334
335设置可显示的最大行数或列数。设置为小于1的值时,按默认值显示。
336
337当layoutDirection是Row/RowReverse时,表示可显示的最大列数。
338
339当layoutDirection是Column/ColumnReverse时,表示可显示的最大行数。
340
341当maxCount小于minCount时,maxCount和minCount都按默认值处理。
342
343**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
344
345**系统能力:** SystemCapability.ArkUI.ArkUI.Full
346
347**参数:**
348
349| 参数名 | 类型   | 必填 | 说明                                          |
350| ------ | ------ | ---- | --------------------------------------------- |
351| value  | number | 是   | 可显示的最大行数或列数。<br/>默认值:Infinity |
352
353### minCount<sup>8+</sup>
354
355minCount(value: number)
356
357设置可显示的最小行数或列数。设置为小于1的值时,按默认值显示。
358
359当layoutDirection是Row/RowReverse时,表示可显示的最小列数。
360
361当layoutDirection是Column/ColumnReverse时,表示可显示的最小行数。
362
363当minCount大于maxCount时,minCount和maxCount都按默认值处理。
364
365**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
366
367**系统能力:** SystemCapability.ArkUI.ArkUI.Full
368
369**参数:**
370
371| 参数名 | 类型   | 必填 | 说明                                   |
372| ------ | ------ | ---- | -------------------------------------- |
373| value  | number | 是   | 可显示的最小行数或列数。<br/>默认值:1 |
374
375### cellLength<sup>8+</sup>
376
377cellLength(value: number)
378
379设置一行的高度或者一列的宽度。
380
381当layoutDirection是Row/RowReverse时,表示一行的高度。
382
383当layoutDirection是Column/ColumnReverse时,表示一列的宽度。
384
385**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
386
387**系统能力:** SystemCapability.ArkUI.ArkUI.Full
388
389**参数:**
390
391| 参数名 | 类型   | 必填 | 说明                                                    |
392| ------ | ------ | ---- | ------------------------------------------------------- |
393| value  | number | 是   | 一行的高度或者一列的宽度。<br/>默认值:第一个元素的大小 <br/>单位:vp <br/>取值范围:(0, +∞),设置为小于等于0的值时,按默认值显示。 |
394
395### multiSelectable<sup>8+</sup>
396
397multiSelectable(value: boolean)
398
399设置是否开启鼠标框选。开启框选后,可以配合GridItem的selected属性和onSelect事件获取GridItem的选中状态,还可以设置[选中态样式](./ts-universal-attributes-polymorphic-style.md)(无默认选中样式)。
400
401**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
402
403**系统能力:** SystemCapability.ArkUI.ArkUI.Full
404
405**参数:**
406
407| 参数名 | 类型    | 必填 | 说明                                                         |
408| ------ | ------- | ---- | ------------------------------------------------------------ |
409| value  | boolean | 是   | 是否开启鼠标框选。<br/>默认值:false<br/>false:关闭框选。true:开启框选。 |
410
411### supportAnimation<sup>8+</sup>
412
413supportAnimation(value: boolean)
414
415设置是否支持动画。当前支持GridItem拖拽动画。仅在滚动模式下(只设置rowsTemplate、columnsTemplate其中一个)支持动画。<br/>仅在大小规则的Grid中支持拖拽动画,跨行或跨列场景不支持。
416
417supportAnimation动画效果参考[示例5(Grid拖拽场景)](#示例5grid拖拽场景),其他动画效果需要应用自定义拖拽实现。
418
419**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
420
421**系统能力:** SystemCapability.ArkUI.ArkUI.Full
422
423**参数:**
424
425| 参数名 | 类型    | 必填 | 说明                             |
426| ------ | ------- | ---- | -------------------------------- |
427| value  | boolean | 是   | 是否支持动画。设置为true时支持GridItem拖拽动画,设置为false时不支持GridItem拖拽动画。<br/>默认值:false |
428
429### edgeEffect<sup>10+</sup>
430
431edgeEffect(value: EdgeEffect, options?: EdgeEffectOptions)
432
433设置边缘滑动效果。
434
435**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
436
437**系统能力:** SystemCapability.ArkUI.ArkUI.Full
438
439**参数:**
440
441| 参数名                | 类型                                                         | 必填 | 说明                                                         |
442| --------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
443| value                 | [EdgeEffect](ts-appendix-enums.md#edgeeffect)                | 是   | Grid组件的边缘滑动效果,支持弹簧效果和阴影效果。<br/>默认值:EdgeEffect.None |
444| options<sup>11+</sup> | [EdgeEffectOptions](ts-container-scrollable-common.md#edgeeffectoptions11对象说明) | 否   | 组件内容大小小于组件自身时,是否开启滑动效果。设置为{ alwaysEnabled: true }会开启滑动效果,{ alwaysEnabled: false }不开启。<br/>默认值:{ alwaysEnabled: false } |
445
446### enableScrollInteraction<sup>10+</sup>
447
448enableScrollInteraction(value: boolean)
449
450设置是否支持滚动手势。
451
452**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
453
454**系统能力:** SystemCapability.ArkUI.ArkUI.Full
455
456**参数:**
457
458| 参数名 | 类型    | 必填 | 说明                                |
459| ------ | ------- | ---- | ----------------------------------- |
460| value  | boolean | 是   | 是否支持滚动手势。设置为true时可以通过手指或者鼠标滚动,设置为false时无法通过手指或者鼠标滚动,但不影响控制器[Scroller](ts-container-scroll.md#scroller)的滚动接口。<br/>默认值:true |
461
462> **说明:**
463>
464> 组件无法通过鼠标按下拖动操作进行滚动。
465
466### nestedScroll<sup>10+</sup>
467
468nestedScroll(value: NestedScrollOptions)
469
470设置嵌套滚动选项。设置前后两个方向的嵌套滚动模式,实现与父组件的滚动联动。当组件内容大小小于组件自身,且[edgeEffect](#edgeeffect10)的options为{ alwaysEnabled: false }时,组件自身滑动手势不会触发,嵌套滚动属性不会生效,如果其父滚动组件有滑动手势,则会触发父组件的滑动手势。
471
472**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
473
474**系统能力:** SystemCapability.ArkUI.ArkUI.Full
475
476**参数:**
477
478| 参数名 | 类型                                                         | 必填 | 说明           |
479| ------ | ------------------------------------------------------------ | ---- | -------------- |
480| value  | [NestedScrollOptions](ts-container-scrollable-common.md#nestedscrolloptions10对象说明) | 是   | 嵌套滚动选项。 |
481
482### friction<sup>10+</sup>
483
484friction(value: number | Resource)
485
486设置摩擦系数,手动划动滚动区域时生效,仅影响惯性滚动过程,对惯性滚动过程中的链式效果有间接影响。
487
488**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
489
490**系统能力:** SystemCapability.ArkUI.ArkUI.Full
491
492**参数:**
493
494| 参数名 | 类型                                                 | 必填 | 说明                                                        |
495| ------ | ---------------------------------------------------- | ---- | ----------------------------------------------------------- |
496| value  | number&nbsp;\|&nbsp;[Resource](ts-types.md#resource) | 是   | 摩擦系数。<br/>默认值:非可穿戴设备为0.6,可穿戴设备为0.9。<br/>从API version 11开始,非可穿戴设备默认值为0.7。<br/>从API version 12开始,非可穿戴设备默认值为0.75。<br/>取值范围:(0, +∞),设置为小于等于0的值时,按默认值处理。 |
497
498### alignItems<sup>12+</sup>
499
500alignItems(alignment: Optional\<GridItemAlignment\>)
501
502设置Grid中GridItem的对齐方式, 使用方法可以参考[示例9](#示例9以当前行最高的griditem的高度为其他griditem的高度)。
503
504**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
505
506**系统能力:** SystemCapability.ArkUI.ArkUI.Full
507
508**参数:**
509
510| 参数名     | 类型   | 必填 | 说明                            |
511| ---------- | ------ | ---- | ------------------------------- |
512| alignment | Optional\<[GridItemAlignment](#griditemalignment12枚举说明)\> | 是   | 设置Grid中GridItem的对齐方式。<br/>默认值:GridItemAlignment.DEFAULT |
513
514### focusWrapMode<sup>20+</sup>
515
516focusWrapMode(mode: Optional\<FocusWrapMode\>)
517
518设置交叉轴方向键走焦模式。
519
520**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。
521
522**系统能力:** SystemCapability.ArkUI.ArkUI.Full
523
524**参数:**
525
526| 参数名 | 类型                                                         | 必填 | 说明                                                         |
527| ------ | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
528| mode   | Optional\<[FocusWrapMode](ts-appendix-enums.md#focuswrapmode20)\> | 是   | 交叉轴方向键走焦模式。<br/>默认值:FocusWrapMode.DEFAULT<br/>**说明:** <br/>异常值按默认值处理,即交叉轴方向键不能换行。 |
529
530### syncLoad<sup>20+</sup>
531
532syncLoad(enable: boolean)
533
534设置是否同步加载Grid区域内所有子组件。
535
536**原子化服务API:** 从API version 20开始,该接口支持在原子化服务中使用。
537
538**系统能力:** SystemCapability.ArkUI.ArkUI.Full
539
540**参数:**
541
542| 参数名 | 类型                                                         | 必填 | 说明                                                         |
543| ------ | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
544| enable   | boolean | 是   | 是否同步加载Grid区域内所有子组件。<br/> true表示同步加载,false表示异步加载。默认值:true。<br/> **说明:** <br/>设置为false时,在首次显示、不带动画scrollToIndex跳转场景,若当帧布局耗时超过50ms,会将Grid区域内尚未布局的子组件延后到下一帧进行布局。 |
545
546## GridItemAlignment<sup>12+</sup>枚举说明
547
548GridItem的对齐方式枚举。
549
550**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
551
552**系统能力:** SystemCapability.ArkUI.ArkUI.Full
553
554| 名称   | 值 | 说明                                 |
555| ------ |------| -------------------------------------- |
556| DEFAULT  |  0  | 使用Grid的默认对齐方式。 |
557| STRETCH |  1  | 以一行中的最高的GridItem作为其他GridItem的高度。 |
558
559
560> **说明:**
561>
562> 1、只有可滚动的Grid中,设置STRETCH参数会生效,其他场景不生效。<br/>
563> 2、在Grid的一行中,如果每个GridItem都是大小规律的(只占一行一列),设置STRETCH参数会生效,存在跨行或跨列的GridItem的场景不生效。<br/>
564> 3、设置STRETCH后,只有不设置高度的GridItem才会以当前行中最高的GridItem作为自己的高度,设置过高度的GridItem高度不会变化。<br/>
565> 4、设置STRETCH后,Grid布局时会有额外的布局流程,可能会带来额外的性能开销。
566
567## GridDirection<sup>8+</sup>枚举说明
568
569主轴布局方向枚举。
570
571**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
572
573**系统能力:** SystemCapability.ArkUI.ArkUI.Full
574
575| 名称   |值| 说明                                 |
576| ------ |------| -------------------------------------- |
577| Row  |  0  | 主轴布局方向沿水平方向布局,即自左往右先填满一行,再去填下一行。 |
578| Column |  1  | 主轴布局方向沿垂直方向布局,即自上往下先填满一列,再去填下一列。 |
579| RowReverse    |  2  | 主轴布局方向沿水平方向反向布局,即自右往左先填满一行,再去填下一行。 |
580| ColumnReverse   |  3  | 主轴布局方向沿垂直方向反向布局,即自下往上先填满一列,再去填下一列。 |
581
582## 事件
583
584除支持[通用事件](ts-component-general-events.md)和[滚动组件通用事件](ts-container-scrollable-common.md#事件)外,还支持以下事件:
585
586### onScrollIndex
587
588onScrollIndex(event: (first: number, last: number) => void)
589
590当前网格显示的起始位置/终止位置的item发生变化时触发。网格初始化时会触发一次。Grid显示区域上第一个子组件/最后一个组件的索引值有变化就会触发。
591
592**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
593
594**系统能力:** SystemCapability.ArkUI.ArkUI.Full
595
596**参数:**
597
598| 参数名             | 类型   | 必填 | 说明                             |
599| ------------------ | ------ | ---- | -------------------------------- |
600| first              | number | 是   | 当前显示的网格起始位置的索引值。 |
601| last<sup>10+</sup> | number | 是   | 当前显示的网格终止位置的索引值。 |
602
603### onItemDragStart<sup>8+</sup>
604
605onItemDragStart(event: (event: ItemDragInfo, itemIndex: number) => (() => any) \| void)
606
607开始拖拽网格元素时触发。返回void表示不能拖拽。
608
609手指长按GridItem时触发该事件。
610
611由于拖拽检测也需要长按,且事件处理机制优先触发子组件事件,GridItem上绑定LongPressGesture时无法触发拖拽。如有长按和拖拽同时使用的需求可以使用通用拖拽事件。
612
613拖拽浮起的网格元素可在应用窗口内移动,若需限制移动范围,可通过自定义手势实现,具体参考[示例16(实现GridItem自定义拖拽)](#示例16实现griditem自定义拖拽)。
614
615**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
616
617**系统能力:** SystemCapability.ArkUI.ArkUI.Full
618
619**参数:**
620
621| 参数名    | 类型                                  | 必填 | 说明                   |
622| --------- | ------------------------------------- | ---- | ---------------------- |
623| event     | [ItemDragInfo](ts-container-scrollable-common.md#itemdraginfo对象说明) | 是   | 拖拽点的信息。         |
624| itemIndex | number                                | 是   | 被拖拽网格元素索引值。 |
625
626### onItemDragEnter<sup>8+</sup>
627
628onItemDragEnter(event: (event: ItemDragInfo) => void)
629
630拖拽进入网格元素范围内时触发。
631
632**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
633
634**系统能力:** SystemCapability.ArkUI.ArkUI.Full
635
636**参数:**
637
638| 参数名 | 类型                                  | 必填 | 说明           |
639| ------ | ------------------------------------- | ---- | -------------- |
640| event  | [ItemDragInfo](ts-container-scrollable-common.md#itemdraginfo对象说明) | 是   | 拖拽点的信息。 |
641
642### onItemDragMove<sup>8+</sup>
643
644onItemDragMove(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number) => void)
645
646拖拽在网格元素范围内移动时触发。
647
648**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
649
650**系统能力:** SystemCapability.ArkUI.ArkUI.Full
651
652**参数:**
653
654| 参数名      | 类型                                  | 必填 | 说明           |
655| ----------- | ------------------------------------- | ---- | -------------- |
656| event       | [ItemDragInfo](ts-container-scrollable-common.md#itemdraginfo对象说明) | 是   | 拖拽点的信息。 |
657| itemIndex   | number                                | 是   | 拖拽起始位置。 |
658| insertIndex | number                                | 是   | 拖拽插入位置。 |
659
660### onItemDragLeave<sup>8+</sup>
661
662onItemDragLeave(event: (event: ItemDragInfo, itemIndex: number) => void)
663
664拖拽离开网格元素时触发。
665
666**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
667
668**系统能力:** SystemCapability.ArkUI.ArkUI.Full
669
670**参数:**
671
672| 参数名    | 类型                                  | 必填 | 说明                       |
673| --------- | ------------------------------------- | ---- | -------------------------- |
674| event     | [ItemDragInfo](ts-container-scrollable-common.md#itemdraginfo对象说明) | 是   | 拖拽点的信息。             |
675| itemIndex | number                                | 是   | 拖拽离开的网格元素索引值。 |
676
677### onItemDrop<sup>8+</sup>
678
679onItemDrop(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => void)
680
681绑定该事件的网格元素可作为拖拽释放目标,当GridItem停止拖拽时触发。
682
683当拖拽释放位置在网格元素之内时,isSuccess会返回true;在网格元素之外时,isSuccess会返回false。
684
685**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
686
687**系统能力:** SystemCapability.ArkUI.ArkUI.Full
688
689**参数:**
690
691| 参数名      | 类型                                  | 必填 | 说明           |
692| ----------- | ------------------------------------- | ---- | -------------- |
693| event       | [ItemDragInfo](ts-container-scrollable-common.md#itemdraginfo对象说明) | 是   | 拖拽点的信息。 |
694| itemIndex   | number                                | 是   | 拖拽起始位置。 |
695| insertIndex | number                                | 是   | 拖拽插入位置。 |
696| isSuccess   | boolean                               | 是   | 是否成功释放   |
697
698### onScrollBarUpdate<sup>10+</sup>
699
700onScrollBarUpdate(event: (index: number, offset: number) => ComputedBarAttribute)
701
702在Grid每帧布局结束时触发,可通过该回调设置滚动条的位置及长度。
703
704该接口只用作设置Grid的滚动条位置,不建议开发者在此接口中做业务逻辑处理。
705
706**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
707
708**系统能力:** SystemCapability.ArkUI.ArkUI.Full
709
710**参数:**
711
712| 参数名 | 类型   | 必填 | 说明                                                         |
713| ------ | ------ | ---- | ------------------------------------------------------------ |
714| index  | number | 是   | 当前显示的网格起始位置的索引值。                             |
715| offset | number | 是   | 当前显示的网格起始位置元素相对网格显示起始位置的偏移,单位vp。 |
716
717**返回值:**
718
719| 类型                                                  | 说明                 |
720| ----------------------------------------------------- | -------------------- |
721| [ComputedBarAttribute](#computedbarattribute10对象说明) | 滚动条的位置及长度。 |
722
723### onReachStart<sup>10+</sup>
724
725onReachStart(event: () => void)
726
727网格到达起始位置时触发。
728
729Grid初始化时会触发一次,Grid滚动到起始位置时触发一次。Grid边缘效果为弹簧效果时,划动经过起始位置时触发一次,回弹回起始位置时再触发一次。
730
731**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
732
733**系统能力:** SystemCapability.ArkUI.ArkUI.Full
734
735**参数:**
736
737| 参数名 | 类型 | 必填 | 说明 |
738| ------ | ------ | ------ | ------|
739| event | () => void | 是 | 网格到达起始位置时触发的回调。 |
740
741### onReachEnd<sup>10+</sup>
742
743onReachEnd(event: () => void)
744
745网格到达末尾位置时触发。不满一屏并且最后一个子组件末端在Grid内时触发。
746
747Grid边缘效果为弹簧效果时,划动经过末尾位置时触发一次,回弹回末尾位置时再触发一次。
748
749**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
750
751**系统能力:** SystemCapability.ArkUI.ArkUI.Full
752
753**参数:**
754
755| 参数名 | 类型 | 必填 | 说明 |
756| ------ | ------ | ------ | ------|
757| event | () => void | 是 | 网格到达末尾位置时触发的回调。 |
758
759### onScrollFrameBegin<sup>10+</sup>
760
761onScrollFrameBegin(event: OnScrollFrameBeginCallback)
762
763该接口回调时,事件参数传入即将发生的滑动量,事件处理函数中可根据应用场景计算实际需要的滑动量并作为事件处理函数的返回值返回,网格将按照返回值的实际滑动量进行滑动。
764
765满足以下任一条件时触发该事件:
766
7671. 用户交互(如手指滑动、键鼠操作等)触发滚动。
7682. Grid惯性滚动。
7693. 调用[fling](ts-container-scroll.md#fling12)接口触发滚动。
770
771不触发该事件的条件:
772
7731. 调用除[fling](ts-container-scroll.md#fling12)接口外的其他滚动控制接口。
7742. 越界回弹。
7753. 拖动滚动条。
776
777**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
778
779**系统能力:** SystemCapability.ArkUI.ArkUI.Full
780
781**参数:**
782
783| 参数名 | 类型                                                    | 必填 | 说明                       |
784| ------ | ------------------------------------------------------- | ---- | -------------------------- |
785| event | [OnScrollFrameBeginCallback](ts-container-scroll.md#onscrollframebegincallback18)   | 是   | 每帧滚动开始回调函数。 |
786
787### onScrollStart<sup>10+</sup>
788
789onScrollStart(event: () => void)
790
791网格滑动开始时触发。手指拖动网格或网格的滚动条触发的滑动开始时,会触发该事件。使用[Scroller](ts-container-scroll.md#scroller)滑动控制器触发的带动画的滑动,动画开始时会触发该事件。
792
793**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
794
795**系统能力:** SystemCapability.ArkUI.ArkUI.Full
796
797**参数:**
798
799| 参数名 | 类型 | 必填 | 说明 |
800| ------ | ------ | ------ | ------|
801| event | () => void | 是 | 网格滑动开始时触发的回调。 |
802
803### onScrollStop<sup>10+</sup>
804
805onScrollStop(event: () => void)
806
807网格滑动停止时触发。手指拖动网格或网格的滚动条触发的滑动,手指离开屏幕后滑动停止时会触发该事件。使用[Scroller](ts-container-scroll.md#scroller)滑动控制器触发的带动画的滑动,动画停止会触发该事件。
808
809**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
810
811**系统能力:** SystemCapability.ArkUI.ArkUI.Full
812
813**参数:**
814
815| 参数名 | 类型 | 必填 | 说明 |
816| ------ | ------ | ------ | ------|
817| event | () => void | 是 | 网格滑动停止时触发的回调。 |
818
819### onScroll<sup>(deprecated)</sup>
820onScroll(event: (scrollOffset: number, scrollState: [ScrollState](ts-container-list.md#scrollstate枚举说明)) => void)
821
822网格滑动时触发。
823
824从API version 10开始使用。
825
826从API version 12开始废弃不再使用,建议使用[onDidScroll](ts-container-scrollable-common.md#ondidscroll12)替代。
827
828**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
829
830**系统能力:** SystemCapability.ArkUI.ArkUI.Full
831
832**参数:**
833
834| 参数名 | 类型 | 必填 | 说明 |
835| ------ | ------ | ------ | ------|
836| scrollOffset | number | 是 | 每帧滚动的偏移量,Grid的内容向上滚动时偏移量为正,向下滚动时偏移量为负。<br/>单位vp。 |
837| scrollState | [ScrollState](ts-container-list.md#scrollstate枚举说明) | 是 | 当前滑动状态。 |
838
839## ComputedBarAttribute<sup>10+</sup>对象说明
840
841滚动条位置和长度对象。
842
843**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
844
845**系统能力:** SystemCapability.ArkUI.ArkUI.Full
846
847| 名称         | 类型         | 只读 | 可选 |   说明         |
848| ----------- | ------------ | ---- | ---- | ---------- |
849| totalOffset | number | 否 | 否 |  Grid内容相对显示区域的总偏移,单位px。    |
850| totalLength   | number | 否 | 否 |  Grid内容总长度,单位px。    |
851
852## UIGridEvent<sup>19+</sup>
853frameNode中[getEvent('Grid')](../js-apis-arkui-frameNode.md#geteventgrid19)方法的返回值,可用于给Grid节点设置滚动事件。
854
855UIGridEvent继承于[UIScrollableCommonEvent](./ts-container-scrollable-common.md#uiscrollablecommonevent19)。
856
857### setOnWillScroll<sup>19+</sup>
858
859setOnWillScroll(callback:  OnWillScrollCallback | undefined): void
860
861设置[onWillScroll](./ts-container-scrollable-common.md#onwillscroll12)事件的回调。
862
863方法入参为undefined时,会重置事件回调。
864
865**原子化服务API:** 从API version 19开始,该接口支持在原子化服务中使用。
866
867**系统能力:** SystemCapability.ArkUI.ArkUI.Full
868
869**参数:**
870
871| 参数名 | 类型   | 必填 | 说明                       |
872| ------ | ------ | ---- | -------------------------- |
873| callback  | [OnWillScrollCallback](./ts-container-scrollable-common.md#onwillscrollcallback12)&nbsp;\|&nbsp;undefined | 是   | onWillScroll事件的回调函数。 |
874
875### setOnDidScroll<sup>19+</sup>
876
877setOnDidScroll(callback: OnScrollCallback | undefined): void
878
879设置[onDidScroll](./ts-container-scrollable-common.md#ondidscroll12)事件的回调。
880
881方法入参为undefined时,会重置事件回调。
882
883**原子化服务API:** 从API version 19开始,该接口支持在原子化服务中使用。
884
885**系统能力:** SystemCapability.ArkUI.ArkUI.Full
886
887**参数:**
888
889| 参数名 | 类型   | 必填 | 说明                       |
890| ------ | ------ | ---- | -------------------------- |
891| callback  | [OnScrollCallback](./ts-container-scrollable-common.md#onscrollcallback12)&nbsp;\|&nbsp;undefined | 是   | onDidScroll事件的回调函数。 |
892
893### setOnScrollIndex<sup>19+</sup>
894
895setOnScrollIndex(callback: OnGridScrollIndexCallback | undefined): void
896
897设置[onScrollIndex](#onscrollindex)事件的回调。
898
899方法入参为undefined时,会重置事件回调。
900
901**原子化服务API:** 从API version 19开始,该接口支持在原子化服务中使用。
902
903**系统能力:** SystemCapability.ArkUI.ArkUI.Full
904
905**参数:**
906
907| 参数名 | 类型   | 必填 | 说明                       |
908| ------ | ------ | ---- | -------------------------- |
909| callback  | [OnGridScrollIndexCallback](#ongridscrollindexcallback19)&nbsp;\|&nbsp;undefined | 是   | onScrollIndex事件的回调函数。 |
910
911## OnGridScrollIndexCallback<sup>19+</sup>
912type OnGridScrollIndexCallback = (first: number, last: number) => void
913
914Grid组件可见区域item变化事件的回调类型。
915
916**原子化服务API:** 从API version 19开始,该接口支持在原子化服务中使用。
917
918**系统能力:** SystemCapability.ArkUI.ArkUI.Full
919
920**参数:**
921
922| 参数名 | 类型 | 必填 | 说明 |
923| ------ | ------ | ------ | ------|
924| first | number | 是 | 当前显示的Grid起始位置的索引值。 |
925| last | number | 是 | 当前显示的Grid终止位置的索引值。 |
926
927## 示例
928
929### 示例1(固定行列Grid)
930
931可以使用GridLayoutOptions中的onGetRectByIndex指定GridItem的位置和大小。
932
933```ts
934// xxx.ets
935@Entry
936@Component
937struct GridExample {
938  @State numbers1: string[] = ['0', '1', '2', '3', '4'];
939  @State numbers2: string[] = ['0', '1', '2', '3', '4', '5'];
940  layoutOptions3: GridLayoutOptions = {
941    regularSize: [1, 1],
942    onGetRectByIndex: (index: number) => {
943      if (index == 0) {
944        return [0, 0, 1, 1];
945      } else if (index == 1) {
946        return [0, 1, 2, 2];
947      } else if (index == 2) {
948        return [0, 3, 3, 3];
949      } else if (index == 3) {
950        return [3, 0, 3, 3];
951      } else if (index == 4) {
952        return [4, 3, 2, 2];
953      } else {
954        return [5, 5, 1, 1];
955      }
956    }
957  };
958
959  build() {
960    Column({ space: 5 }) {
961      Grid() {
962        ForEach(this.numbers1, (day: string) => {
963          ForEach(this.numbers1, (day: string) => {
964            GridItem() {
965              Text(day)
966                .fontSize(16)
967                .backgroundColor(0xF9CF93)
968                .width('100%')
969                .height('100%')
970                .textAlign(TextAlign.Center)
971            }
972          }, (day: string) => day)
973        }, (day: string) => day)
974      }
975      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
976      .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
977      .columnsGap(10)
978      .rowsGap(10)
979      .width('90%')
980      .backgroundColor(0xFAEEE0)
981      .height(300)
982
983      Text('GridLayoutOptions的使用:onGetRectByIndex。').fontColor(0xCCCCCC).fontSize(9).width('90%')
984
985      Grid(undefined, this.layoutOptions3) {
986        ForEach(this.numbers2, (day: string) => {
987          GridItem() {
988            Text(day)
989              .fontSize(16)
990              .backgroundColor(0xF9CF93)
991              .width('100%')
992              .height('100%')
993              .textAlign(TextAlign.Center)
994          }
995          .height('100%')
996          .width('100%')
997        }, (day: string) => day)
998      }
999      .columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
1000      .rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
1001      .columnsGap(10)
1002      .rowsGap(10)
1003      .width('90%')
1004      .backgroundColor(0xFAEEE0)
1005      .height(300)
1006    }.width('100%').margin({ top: 5 })
1007  }
1008}
1009```
1010
1011![zh-cn_image_0000001219744183](figures/zh-cn_image_0000001219744183.gif)
1012
1013### 示例2(可滚动Grid和滚动事件)
1014
1015可滚动Grid,包括所有滚动属性和事件。
1016
1017GridDataSource实现了LazyForEach数据源接口[IDataSource](ts-rendering-control-lazyforeach.md#idatasource),用于通过LazyForEach给Grid提供子组件。
1018
1019<!--code_no_check-->
1020```ts
1021// GridDataSource.ets
1022export class GridDataSource implements IDataSource {
1023  private list: string[] = [];
1024  private listeners: DataChangeListener[] = [];
1025
1026  constructor(list: string[]) {
1027    this.list = list;
1028  }
1029
1030  totalCount(): number {
1031    return this.list.length;
1032  }
1033
1034  getData(index: number): string {
1035    return this.list[index];
1036  }
1037
1038  registerDataChangeListener(listener: DataChangeListener): void {
1039    if (this.listeners.indexOf(listener) < 0) {
1040      this.listeners.push(listener);
1041    }
1042  }
1043
1044  unregisterDataChangeListener(listener: DataChangeListener): void {
1045    const pos = this.listeners.indexOf(listener);
1046    if (pos >= 0) {
1047      this.listeners.splice(pos, 1);
1048    }
1049  }
1050
1051  // 通知控制器数据位置变化
1052  notifyDataMove(from: number, to: number): void {
1053    this.listeners.forEach(listener => {
1054      listener.onDataMove(from, to);
1055    })
1056  }
1057
1058  // 交换元素位置
1059  public swapItem(from: number, to: number): void {
1060    let temp: string = this.list[from];
1061    this.list[from] = this.list[to];
1062    this.list[to] = temp;
1063    this.notifyDataMove(from, to);
1064  }
1065}
1066```
1067
1068<!--code_no_check-->
1069```ts
1070// xxx.ets
1071import { GridDataSource } from './GridDataSource';
1072
1073@Entry
1074@Component
1075struct GridExample {
1076  numbers: GridDataSource = new GridDataSource([]);
1077  scroller: Scroller = new Scroller();
1078  @State gridPosition: number = 0; //0代表滚动到grid顶部,1代表中间值,2代表滚动到grid底部。
1079
1080  aboutToAppear() {
1081    let list: string[] = [];
1082    for (let i = 0; i < 5; i++) {
1083      for (let j = 0; j < 5; j++) {
1084        list.push(j.toString());
1085      }
1086    }
1087    this.numbers = new GridDataSource(list);
1088  }
1089
1090  build() {
1091    Column({ space: 5 }) {
1092      Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')
1093      Grid(this.scroller) {
1094        LazyForEach(this.numbers, (day: string) => {
1095          GridItem() {
1096            Text(day)
1097              .fontSize(16)
1098              .backgroundColor(0xF9CF93)
1099              .width('100%')
1100              .height(80)
1101              .textAlign(TextAlign.Center)
1102          }
1103        }, (index: number) => index.toString())
1104      }
1105      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
1106      .columnsGap(10)
1107      .rowsGap(10)
1108      .friction(0.6)
1109      .enableScrollInteraction(true)
1110      .supportAnimation(false)
1111      .multiSelectable(false)
1112      .edgeEffect(EdgeEffect.Spring)
1113      .scrollBar(BarState.On)
1114      .scrollBarColor(Color.Grey)
1115      .scrollBarWidth(4)
1116      .width('90%')
1117      .backgroundColor(0xFAEEE0)
1118      .height(300)
1119      .onScrollIndex((first: number, last: number) => {
1120        console.info(first.toString());
1121        console.info(last.toString());
1122      })
1123      .onScrollBarUpdate((index: number, offset: number) => {
1124        console.info("XXX" + 'Grid onScrollBarUpdate,index : ' + index.toString() + ",offset" + offset.toString());
1125        return { totalOffset: (index / 5) * (80 + 10) - offset, totalLength: 80 * 5 + 10 * 4 };
1126      })  //只适用于当前示例代码数据源,如果数据源有变化,则需要修改该部分代码,或者删掉此属性
1127      .onDidScroll((scrollOffset: number, scrollState: ScrollState) => {
1128        console.info(scrollOffset.toString());
1129        console.info(scrollState.toString());
1130      })
1131      .onScrollStart(() => {
1132        console.info("XXX" + "Grid onScrollStart");
1133      })
1134      .onScrollStop(() => {
1135        console.info("XXX" + "Grid onScrollStop");
1136      })
1137      .onReachStart(() => {
1138        this.gridPosition = 0;
1139        console.info("XXX" + "Grid onReachStart");
1140      })
1141      .onReachEnd(() => {
1142        this.gridPosition = 2;
1143        console.info("XXX" + "Grid onReachEnd");
1144      })
1145
1146      Button('next page')
1147        .onClick(() => { // 点击后滑到下一页
1148          this.scroller.scrollPage({ next: true });
1149        })
1150    }.width('100%').margin({ top: 5 })
1151  }
1152}
1153```
1154
1155![scrollerExample2](figures/scrollerExample2.gif)
1156
1157### 示例3(可滚动Grid设置跨行跨列节点)
1158
1159GridLayoutOptions的使用:irregularIndexes与onGetIrregularSizeByIndex。
1160
1161GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1162
1163<!--code_no_check-->
1164```ts
1165// xxx.ets
1166import { GridDataSource } from './GridDataSource';
1167
1168@Entry
1169@Component
1170struct GridExample {
1171  numbers: GridDataSource = new GridDataSource([]);
1172  scroller: Scroller = new Scroller();
1173  layoutOptions1: GridLayoutOptions = {
1174    regularSize: [1, 1],        // 只支持[1, 1]
1175    irregularIndexes: [0, 6],   // 索引为0和6的GridItem占用一行
1176  };
1177
1178  layoutOptions2: GridLayoutOptions = {
1179    regularSize: [1, 1],
1180    irregularIndexes: [0, 7],   // 索引为0和7的GridItem占用的列数由onGetIrregularSizeByIndex指定
1181    onGetIrregularSizeByIndex: (index: number) => {
1182      if (index === 0) {
1183        return [1, 5];
1184      }
1185      return [1, index % 6 + 1];
1186    }
1187  };
1188
1189  aboutToAppear() {
1190    let list: string[] = [];
1191    for (let i = 0; i < 5; i++) {
1192      for (let j = 0; j < 5; j++) {
1193        list.push(j.toString());
1194      }
1195    }
1196    this.numbers = new GridDataSource(list);
1197  }
1198
1199  build() {
1200    Column({ space: 5 }) {
1201      Grid(this.scroller, this.layoutOptions1) {
1202        LazyForEach(this.numbers, (day: string) => {
1203          GridItem() {
1204            Text(day)
1205              .fontSize(16)
1206              .backgroundColor(0xF9CF93)
1207              .width('100%')
1208              .height(80)
1209              .textAlign(TextAlign.Center)
1210          }.selectable(false)
1211        }, (index: number) => index.toString())
1212      }
1213      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
1214      .columnsGap(10)
1215      .rowsGap(10)
1216      .multiSelectable(true)
1217      .scrollBar(BarState.Off)
1218      .width('90%')
1219      .backgroundColor(0xFAEEE0)
1220      .height(300)
1221
1222      Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')
1223      // 不使用scroll,需要undefined占位
1224      Grid(undefined, this.layoutOptions2) {
1225        LazyForEach(this.numbers, (day: string) => {
1226          GridItem() {
1227            Text(day)
1228              .fontSize(16)
1229              .backgroundColor(0xF9CF93)
1230              .width('100%')
1231              .height(80)
1232              .textAlign(TextAlign.Center)
1233          }
1234        }, (index: number) => index.toString())
1235      }
1236      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
1237      .columnsGap(10)
1238      .rowsGap(10)
1239      .scrollBar(BarState.Off)
1240      .width('90%')
1241      .backgroundColor(0xFAEEE0)
1242      .height(300)
1243    }.width('100%').margin({ top: 5 })
1244  }
1245}
1246```
1247
1248![gridLayoutOptions](figures/gridLayoutOptions.gif)
1249
1250### 示例4(Grid嵌套滚动)
1251
1252nestedScroll和onScrollFrameBegin的使用。
1253
1254GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1255
1256<!--code_no_check-->
1257```ts
1258import { GridDataSource } from './GridDataSource';
1259
1260@Entry
1261@Component
1262struct GridExample {
1263  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
1264  numbers: GridDataSource = new GridDataSource([]);
1265  @State translateY: number = 0;
1266  private scroller: Scroller = new Scroller();
1267  private gridScroller: Scroller = new Scroller();
1268  private touchDown: boolean = false;
1269  private listTouchDown: boolean = false;
1270  private scrolling: boolean = false;
1271
1272  aboutToAppear() {
1273    let list: string[] = [];
1274    for (let i = 0; i < 100; i++) {
1275      list.push(i.toString());
1276    }
1277    this.numbers = new GridDataSource(list);
1278  }
1279
1280  build() {
1281    Stack() {
1282      Column() {
1283        Row() {
1284          Text('Head')
1285        }
1286
1287        Column() {
1288          List({ scroller: this.scroller }) {
1289            ListItem() {
1290              Grid() {
1291                GridItem() {
1292                  Text('GoodsTypeList1')
1293                }
1294                .backgroundColor(this.colors[0])
1295                .columnStart(0)
1296                .columnEnd(1)
1297
1298                GridItem() {
1299                  Text('GoodsTypeList2')
1300                }
1301                .backgroundColor(this.colors[1])
1302                .columnStart(0)
1303                .columnEnd(1)
1304
1305                GridItem() {
1306                  Text('GoodsTypeList3')
1307                }
1308                .backgroundColor(this.colors[2])
1309                .columnStart(0)
1310                .columnEnd(1)
1311
1312                GridItem() {
1313                  Text('GoodsTypeList4')
1314                }
1315                .backgroundColor(this.colors[3])
1316                .columnStart(0)
1317                .columnEnd(1)
1318
1319                GridItem() {
1320                  Text('GoodsTypeList5')
1321                }
1322                .backgroundColor(this.colors[4])
1323                .columnStart(0)
1324                .columnEnd(1)
1325              }
1326              .scrollBar(BarState.Off)
1327              .columnsGap(15)
1328              .rowsGap(10)
1329              .rowsTemplate('1fr 1fr 1fr 1fr 1fr')
1330              .columnsTemplate('1fr')
1331              .width('100%')
1332              .height(200)
1333            }
1334
1335            ListItem() {
1336              Grid(this.gridScroller) {
1337                LazyForEach(this.numbers, (item: string) => {
1338                  GridItem() {
1339                    Text(item)
1340                      .fontSize(16)
1341                      .backgroundColor(0xF9CF93)
1342                      .width('100%')
1343                      .height('100%')
1344                      .textAlign(TextAlign.Center)
1345                  }
1346                  .width('100%')
1347                  .height(40)
1348                  .shadow({ radius: 10, color: '#909399', offsetX: 1, offsetY: 1 })
1349                  .borderRadius(10)
1350                  .translate({ x: 0, y: this.translateY })
1351                }, (item: string) => item)
1352              }
1353              .columnsTemplate('1fr 1fr')
1354              .friction(0.3)
1355              .columnsGap(15)
1356              .rowsGap(10)
1357              .scrollBar(BarState.Off)
1358              .width('100%')
1359              .height('100%')
1360              .layoutDirection(GridDirection.Column)
1361              .nestedScroll({
1362                scrollForward: NestedScrollMode.PARENT_FIRST,
1363                scrollBackward: NestedScrollMode.SELF_FIRST
1364              })
1365              .onTouch((event: TouchEvent) => {
1366                if (event.type == TouchType.Down) {
1367                  this.listTouchDown = true;
1368                } else if (event.type == TouchType.Up) {
1369                  this.listTouchDown = false;
1370                }
1371              })
1372            }
1373          }
1374          .scrollBar(BarState.Off)
1375          .edgeEffect(EdgeEffect.None)
1376          .onTouch((event: TouchEvent) => {
1377            if (event.type == TouchType.Down) {
1378              this.touchDown = true;
1379            } else if (event.type == TouchType.Up) {
1380              this.touchDown = false;
1381            }
1382          })
1383          .onScrollFrameBegin((offset: number, state: ScrollState) => {
1384            if (this.scrolling && offset > 0) {
1385              let newOffset = this.scroller.currentOffset().yOffset;
1386              if (newOffset >= 590) {
1387                this.gridScroller.scrollBy(0, offset);
1388                return { offsetRemain: 0 };
1389              } else if (newOffset + offset > 590) {
1390                this.gridScroller.scrollBy(0, newOffset + offset - 590);
1391                return { offsetRemain: 590 - newOffset };
1392              }
1393            }
1394            return { offsetRemain: offset };
1395          })
1396          .onScrollStart(() => {
1397            if (this.touchDown && !this.listTouchDown) {
1398              this.scrolling = true;
1399            }
1400          })
1401          .onScrollStop(() => {
1402            this.scrolling = false;
1403          })
1404        }
1405        .width('100%')
1406        .height('100%')
1407        .padding({ left: 10, right: 10 })
1408      }
1409
1410      Row() {
1411        Text('Top')
1412          .width(30)
1413          .height(30)
1414          .borderRadius(50)
1415      }
1416      .padding(5)
1417      .borderRadius(50)
1418      .backgroundColor('#ffffff')
1419      .shadow({ radius: 10, color: '#909399', offsetX: 1, offsetY: 1 })
1420      .margin({ right: 22, bottom: 15 })
1421      .onClick(() => {
1422        this.scroller.scrollTo({ xOffset: 0, yOffset: 0 });
1423        this.gridScroller.scrollTo({ xOffset: 0, yOffset: 0 });
1424      })
1425    }
1426    .align(Alignment.BottomEnd)
1427  }
1428}
1429```
1430
1431![nestedScrollExample4](figures/nestedScrollExample4.gif)
1432
1433### 示例5(Grid拖拽场景)
1434
14351.  设置属性editMode\(true\)设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem。
14362.  在[onItemDragStart](#onitemdragstart8)回调中设置拖拽过程中显示的图片。
14373.  在[onItemDrop](#onitemdrop8)中获取拖拽起始位置,和拖拽插入位置,并在[onItemDrop](#onitemdrop8)中完成交换数组位置逻辑。
14384.  设置属性`supportAnimation(true)`支持动画。
1439
1440> **说明:**
1441>
1442> 预览器窗口不支持显示拖拽跟手。
1443
1444GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1445
1446<!--code_no_check-->
1447```ts
1448import { GridDataSource } from './GridDataSource';
1449
1450@Entry
1451@Component
1452struct GridExample {
1453  numbers: GridDataSource = new GridDataSource([]);
1454  scroller: Scroller = new Scroller();
1455  @State text: string = 'drag';
1456
1457  @Builder pixelMapBuilder() { //拖拽过程样式
1458    Column() {
1459      Text(this.text)
1460        .fontSize(16)
1461        .backgroundColor(0xF9CF93)
1462        .width(80)
1463        .height(80)
1464        .textAlign(TextAlign.Center)
1465    }
1466  }
1467
1468  aboutToAppear() {
1469    let list: string[] = [];
1470    for (let i = 1; i <= 15; i++) {
1471      list.push(i + '');
1472    }
1473    this.numbers = new GridDataSource(list);
1474  }
1475
1476  changeIndex(index1: number, index2: number) { //交换数组位置
1477    this.numbers.swapItem(index1, index2);
1478  }
1479
1480  build() {
1481    Column({ space: 5 }) {
1482      Grid(this.scroller) {
1483        LazyForEach(this.numbers, (day: string) => {
1484          GridItem() {
1485            Text(day)
1486              .fontSize(16)
1487              .backgroundColor(0xF9CF93)
1488              .width(80)
1489              .height(80)
1490              .textAlign(TextAlign.Center)
1491          }
1492        }, (day: string) => day)
1493      }
1494      .columnsTemplate('1fr 1fr 1fr')
1495      .columnsGap(10)
1496      .rowsGap(10)
1497      .width('90%')
1498      .backgroundColor(0xFAEEE0)
1499      .height(300)
1500      .editMode(true) //设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem
1501      .supportAnimation(true) // 设置支持动画
1502      .onItemDragStart((event: ItemDragInfo, itemIndex: number) => { //第一次拖拽此事件绑定的组件时,触发回调。
1503        this.text = this.numbers.getData(itemIndex);
1504        return this.pixelMapBuilder(); //设置拖拽过程中显示的图片。
1505      })
1506      .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { //绑定此事件的组件可作为拖拽释放目标,当在本组件范围内停止拖拽行为时,触发回调。
1507        // isSuccess=false时,说明drop的位置在grid外部;insertIndex > length时,说明有新增元素的事件发生
1508        if (!isSuccess || insertIndex >= this.numbers.totalCount()) {
1509          return;
1510        }
1511        console.info('itemIndex:' + itemIndex + ', insertIndex:' + insertIndex); //itemIndex拖拽起始位置,insertIndex拖拽插入位置
1512        this.changeIndex(itemIndex, insertIndex);
1513      })
1514    }.width('100%').margin({ top: 5 })
1515  }
1516}
1517```
1518
1519示例图:
1520
1521网格子组件开始拖拽:
1522
1523![gridDrag](figures/gridDrag.png)
1524
1525网格子组件拖拽过程中:
1526
1527![gridDrag](figures/gridDrag1.png)
1528
1529网格子组件1与子组件6拖拽交换位置后:
1530
1531![gridDrag](figures/gridDrag2.png)
1532
1533拖拽动画:
1534
1535![gridDragAnimation](figures/gridDragAnimation.gif)
1536
1537### 示例6(自适应Grid)
1538
1539layoutDirection、maxCount、minCount、cellLength的使用。
1540
1541GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1542
1543<!--code_no_check-->
1544```ts
1545import { GridDataSource } from './GridDataSource';
1546
1547@Entry
1548@Component
1549struct GridExample {
1550  numbers: GridDataSource = new GridDataSource([]);
1551
1552  aboutToAppear() {
1553    let list: string[] = [];
1554    for (let i = 1; i <= 30; i++) {
1555      list.push(i + '');
1556    }
1557    this.numbers = new GridDataSource(list);
1558  }
1559
1560  build() {
1561    Scroll() {
1562      Column({ space: 5 }) {
1563        Blank()
1564        Text('rowsTemplate、columnsTemplate都不设置layoutDirection、maxCount、minCount、cellLength才生效')
1565          .fontSize(15).fontColor(0xCCCCCC).width('90%')
1566        Grid() {
1567          LazyForEach(this.numbers, (day: string) => {
1568            GridItem() {
1569              Text(day).fontSize(16).backgroundColor(0xF9CF93)
1570            }.width(40).height(80).borderWidth(2).borderColor(Color.Red)
1571          }, (day: string) => day)
1572        }
1573        .height(300)
1574        .columnsGap(10)
1575        .rowsGap(10)
1576        .backgroundColor(0xFAEEE0)
1577        .maxCount(6)
1578        .minCount(2)
1579        .cellLength(0)
1580        .layoutDirection(GridDirection.Row)
1581      }
1582      .width('90%').margin({ top: 5, left: 5, right: 5 })
1583      .align(Alignment.Center)
1584    }
1585  }
1586}
1587```
1588
1589![cellLength](figures/cellLength.gif)
1590
1591### 示例7(双指缩放修改Grid列数)
1592
1593双指缩放修改Grid列数。
1594
1595GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1596
1597<!--code_no_check-->
1598```ts
1599// xxx.ets
1600import { GridDataSource } from './GridDataSource';
1601
1602@Entry
1603@Component
1604struct GridExample {
1605  numbers: GridDataSource = new GridDataSource([]);
1606  @State columns: number = 2;
1607
1608  aboutToAppear() {
1609    let lastCount = AppStorage.get<number>('columnsCount');
1610    if (typeof lastCount != 'undefined') {
1611      this.columns = lastCount;
1612    }
1613
1614    let list: string[] = [];
1615    for (let i = 0; i < 20; i++) {
1616      for (let j = 0; j < 20; j++) {
1617        list.push(j.toString());
1618      }
1619    }
1620    this.numbers = new GridDataSource(list);
1621  }
1622
1623  build() {
1624    Column({ space: 5 }) {
1625      Row() {
1626        Text('双指缩放改变列数')
1627          .height('5%')
1628          .margin({ top: 10, left: 20 })
1629      }
1630
1631      Grid() {
1632        LazyForEach(this.numbers, (day: string) => {
1633          GridItem() {
1634            Text(day)
1635              .fontSize(16)
1636              .backgroundColor(0xF9CF93)
1637              .width('100%')
1638              .height(80)
1639              .textAlign(TextAlign.Center)
1640          }
1641        }, (index: number) => index.toString())
1642      }
1643      .columnsTemplate('1fr '.repeat(this.columns))
1644      .columnsGap(10)
1645      .rowsGap(10)
1646      .width('90%')
1647      .scrollBar(BarState.Off)
1648      .backgroundColor(0xFAEEE0)
1649      .height('100%')
1650      .cachedCount(3)
1651      // 切换列数item位置重排动画
1652      .animation({
1653        duration: 300,
1654        curve: Curve.Smooth
1655      })
1656      .priorityGesture(
1657        PinchGesture()
1658          .onActionEnd((event: GestureEvent) => {
1659            console.info('end scale:' + event.scale);
1660            // 手指分开,减少列数以放大Item,触发阈值可以自定义,示例为2
1661            if (event.scale > 2) {
1662              this.columns--;
1663            } else if (event.scale < 0.6) {
1664              this.columns++;
1665            }
1666            // 可以根据设备屏幕宽度设定最大和最小列数,此处以最小1列最大4列为例
1667            this.columns = Math.min(4, Math.max(1, this.columns));
1668            AppStorage.setOrCreate<number>('columnsCount', this.columns);
1669          })
1670      )
1671    }.width('100%').margin({ top: 5 })
1672  }
1673}
1674```
1675
1676![pinch](figures/grid-pinch.gif)
1677
1678### 示例8(设置自适应列数)
1679属性[columnsTemplate](#columnstemplate)中auto-fill、auto-fit和auto-stretch的使用示例。
1680
1681```ts
1682@Entry
1683@Component
1684struct GridColumnsTemplate {
1685  data: number[] = [0, 1, 2, 3, 4, 5];
1686  data1: number[] = [0, 1, 2, 3, 4, 5];
1687  data2: number[] = [0, 1, 2, 3, 4, 5];
1688
1689  build() {
1690    Column({ space: 10 }) {
1691      Text('auto-fill 根据设定的列宽自动计算列数').width('90%')
1692      Grid() {
1693        ForEach(this.data, (item: number) => {
1694          GridItem() {
1695            Text('N' + item).height(80)
1696          }
1697          .backgroundColor(Color.Orange)
1698        })
1699      }
1700      .width('90%')
1701      .border({ width: 1, color: Color.Black })
1702      .columnsTemplate('repeat(auto-fill, 70)')
1703      .columnsGap(10)
1704      .rowsGap(10)
1705      .height(150)
1706
1707      Text('auto-fit 先根据设定的列宽计算列数,余下的空间会均分到每一列中').width('90%')
1708      Grid() {
1709        ForEach(this.data1, (item: number) => {
1710          GridItem() {
1711            Text('N' + item).height(80)
1712          }
1713          .backgroundColor(Color.Orange)
1714        })
1715      }
1716      .width('90%')
1717      .border({ width: 1, color: Color.Black })
1718      .columnsTemplate('repeat(auto-fit, 70)')
1719      .columnsGap(10)
1720      .rowsGap(10)
1721      .height(150)
1722
1723      Text('auto-stretch 先根据设定的列宽计算列数,余下的空间会均分到每个列间距中').width('90%')
1724      Grid() {
1725        ForEach(this.data2, (item: number) => {
1726          GridItem() {
1727            Text('N' + item).height(80)
1728          }
1729          .backgroundColor(Color.Orange)
1730        })
1731      }
1732      .width('90%')
1733      .border({ width: 1, color: Color.Black })
1734      .columnsTemplate('repeat(auto-stretch, 70)')
1735      .columnsGap(10)
1736      .rowsGap(10)
1737      .height(150)
1738    }
1739    .width('100%')
1740    .height('100%')
1741  }
1742}
1743```
1744
1745![gridColumnsTemplate](figures/gridColumnsTemplate.png)
1746
1747### 示例9(以当前行最高的GridItem的高度为其他GridItem的高度)
1748下面的Grid中包含两列,每列中的GridItem包括高度确定的两个Column和一个高度不确定的Text共三个子组件。
1749
1750在默认情况下,左右两个GridItem的高度可能是不同的;在设置了Grid的[alignItems](#alignitems12)属性为GridItemAlignment.STRETCH后,一行左右两个GridItem中原本高度较小的GridItem会以另一个高度较大的GridItem的高度作为自己的高度。
1751
1752GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1753
1754<!--code_no_check-->
1755```ts
1756import { GridDataSource } from './GridDataSource';
1757
1758@Entry
1759@Component
1760struct Index {
1761  data: GridDataSource = new GridDataSource([]);
1762  @State items: number[] = [];
1763
1764  aboutToAppear(): void {
1765    let list: string[] = [];
1766    for (let i = 0; i < 100; i++) {
1767      list.push(i.toString());
1768      this.items.push(this.getSize());
1769    }
1770    this.data= new GridDataSource(list);
1771  }
1772
1773  getSize() {
1774    let ret = Math.floor(Math.random() * 5);
1775    return Math.max(1, ret);
1776  }
1777
1778  build() {
1779    Column({ space: 10 }) {
1780      Text('Grid alignItems示例代码')
1781
1782      Grid() {
1783        LazyForEach(this.data, (item: number) => {
1784          // GridItem和Column不设置高度,默认会自适应子组件大小,设置STRETCH的场景下,会变成与当前行最高节点同高。
1785          // 若设置高度,则会保持已设置的高度,不会与当前行最高节点同高。
1786          GridItem() {
1787            Column() {
1788              Column().height(100).backgroundColor('#D5D5D5').width('100%')
1789              // 中间的Text设置flexGrow(1)来自适应填满父组件的空缺
1790              Text('这是一段文字。'.repeat(this.items[item]))
1791                .flexGrow(1).width('100%').align(Alignment.TopStart)
1792                .backgroundColor('#F7F7F7')
1793              Column().height(50).backgroundColor('#707070').width('100%')
1794            }
1795          }
1796          .border({ color: Color.Black, width: 1 })
1797        })
1798      }
1799      .columnsGap(10)
1800      .rowsGap(5)
1801      .columnsTemplate('1fr 1fr')
1802      .width('80%')
1803      .height('100%')
1804      // Grid设置alignItems为STRETCH,以当前行最高的GridItem的高度为其他GridItem的高度。
1805      .alignItems(GridItemAlignment.STRETCH)
1806      .scrollBar(BarState.Off)
1807    }
1808    .height('100%')
1809    .width('100%')
1810  }
1811}
1812
1813```
1814![gridAlignItems](figures/gridAlignItems.png)
1815
1816### 示例10(设置边缘渐隐)
1817通过[fadingEdge](ts-container-scrollable-common.md#fadingedge14)属性来设置边缘渐隐效果。
1818
1819GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1820
1821<!--code_no_check-->
1822
1823```ts
1824// xxx.ets
1825//该示例实现了Grid组件开启边缘渐隐效果并设置边缘渐隐长度
1826import { LengthMetrics } from '@kit.ArkUI';
1827import { GridDataSource } from './GridDataSource';
1828
1829@Entry
1830@Component
1831struct GridExample {
1832  numbers: GridDataSource = new GridDataSource([]);
1833  scroller: Scroller = new Scroller();
1834
1835  aboutToAppear() {
1836    let list: string[] = [];
1837    for (let i = 0; i <= 10; i++) {
1838      for (let j = 0; j < 5; j++) {
1839        list.push(j.toString());
1840      }
1841    }
1842    this.numbers = new GridDataSource(list);
1843  }
1844
1845  build() {
1846    Column({ space: 5 }) {
1847      Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')
1848      Grid(this.scroller) {
1849        LazyForEach(this.numbers, (day: string) => {
1850          GridItem() {
1851            Text(day)
1852              .fontSize(16)
1853              .backgroundColor(0xF9CF93)
1854              .width('100%')
1855              .height(80)
1856              .textAlign(TextAlign.Center)
1857          }
1858        }, (index: number) => index.toString())
1859      }
1860      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
1861      .columnsGap(10)
1862      .rowsGap(20)
1863      .height('90%')
1864      .fadingEdge(true, { fadingEdgeLength: LengthMetrics.vp(80) })
1865
1866    }.width('100%').margin({ top: 5 })
1867  }
1868}
1869```
1870
1871![fadingEdge_grid](figures/fadingEdge_grid.gif)
1872
1873### 示例11(单边边缘效果)
1874
1875该示例通过edgeEffect接口,实现了Grid组件设置单边边缘效果。
1876
1877GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
1878
1879<!--code_no_check-->
1880```ts
1881// xxx.ets
1882import { GridDataSource } from './GridDataSource';
1883
1884@Entry
1885@Component
1886struct GridExample {
1887  numbers: GridDataSource = new GridDataSource([]);
1888  scroller: Scroller = new Scroller();
1889
1890  aboutToAppear() {
1891    let list: string[] = [];
1892    for (let i = 0; i <= 10; i++) {
1893      for (let j = 0; j < 5; j++) {
1894        list.push(j.toString());
1895      }
1896    }
1897    this.numbers = new GridDataSource(list);
1898  }
1899
1900  build() {
1901    Column({ space: 5 }) {
1902      Grid(this.scroller) {
1903        LazyForEach(this.numbers, (day: string) => {
1904          GridItem() {
1905            Text(day)
1906              .fontSize(16)
1907              .backgroundColor(0xF9CF93)
1908              .width('100%')
1909              .height(80)
1910              .textAlign(TextAlign.Center)
1911          }
1912        }, (index: number) => index.toString())
1913      }
1914      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
1915      .columnsGap(10)
1916      .rowsGap(20)
1917      .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true, effectEdge: EffectEdge.START })
1918      .width('90%')
1919      .backgroundColor(0xDCDCDC)
1920      .height('80%')
1921
1922    }.width('100%').margin({ top: 5 })
1923  }
1924}
1925```
1926
1927![edgeEffect_grid](figures/edgeEffect_grid.gif)
1928
1929### 示例12(方向键走焦换行模式)
1930
1931该示例通过focusWrapMode接口,实现了Grid组件方向键走焦换行效果。
1932
1933```ts
1934// xxx.ets
1935@Entry
1936@Component
1937struct GridExample {
1938  scroller: Scroller = new Scroller();
1939  build() {
1940    Column() {
1941      Grid(this.scroller) {
1942        GridItem() {
1943          Text('A')
1944            .focusable(true)
1945            .fontSize(18)
1946            .fontWeight(5)
1947            .backgroundColor(0xF9CF93)
1948            .width('100%')
1949            .height(80)
1950            .textAlign(TextAlign.Center)
1951        }
1952        GridItem() {
1953          Text('B')
1954            .focusable(true)
1955            .fontSize(18)
1956            .fontWeight(5)
1957            .backgroundColor(0xF9CF93)
1958            .width('100%')
1959            .height(80)
1960            .textAlign(TextAlign.Center)
1961        }
1962        GridItem() {
1963          Text('C')
1964            .focusable(true)
1965            .fontSize(18)
1966            .fontWeight(5)
1967            .backgroundColor(0xF9CF93)
1968            .width('100%')
1969            .height(80)
1970            .textAlign(TextAlign.Center)
1971        }
1972        GridItem() {
1973          Text('D')
1974            .focusable(true)
1975            .fontSize(18)
1976            .fontWeight(5)
1977            .backgroundColor(0xF9CF93)
1978            .width('100%')
1979            .height(80)
1980            .textAlign(TextAlign.Center)
1981        }
1982        GridItem() {
1983          Text('E')
1984            .focusable(true)
1985            .fontSize(18)
1986            .fontWeight(5)
1987            .backgroundColor(0xF9CF93)
1988            .width('100%')
1989            .height(80)
1990            .textAlign(TextAlign.Center)
1991        }
1992        GridItem() {
1993          Text('F')
1994            .focusable(true)
1995            .fontSize(18)
1996            .fontWeight(5)
1997            .backgroundColor(0xF9CF93)
1998            .width('100%')
1999            .height(80)
2000            .textAlign(TextAlign.Center)
2001        }
2002      }
2003      .focusWrapMode(FocusWrapMode.WRAP_WITH_ARROW)
2004      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
2005      .columnsGap(10)
2006      .rowsGap(20)
2007      .backgroundColor(0xDCDCDC)
2008    }.width('100%').margin({ top: 5 })
2009  }
2010}
2011```
2012
2013![edgeEffect_grid](figures/gridFocus.gif)
2014
2015### 示例13(滚动事件)
2016
2017```ts
2018import { NodeController, FrameNode, typeNode } from '@kit.ArkUI';
2019
2020class MyNodeController extends NodeController {
2021  public rootNode: FrameNode | null = null;
2022
2023  makeNode(uiContext: UIContext): FrameNode | null {
2024    this.rootNode = new FrameNode(uiContext);
2025    this.rootNode.commonAttribute.width(100);
2026    return this.rootNode;
2027  }
2028
2029  addCommonEvent(frameNode: FrameNode) {
2030    let gridEvent: UIGridEvent | undefined = typeNode.getEvent(frameNode, "Grid");
2031    gridEvent?.setOnWillScroll((scrollOffset: number, scrollState: ScrollState, scrollSource: ScrollSource) => {
2032      console.info(`onWillScroll scrollOffset = ${scrollOffset}, scrollState = ${scrollState}, scrollSource = ${scrollSource}`);
2033    });
2034    gridEvent?.setOnDidScroll((scrollOffset: number, scrollState: ScrollState) => {
2035      console.info(`onDidScroll scrollOffset = ${scrollOffset}, scrollState = ${scrollState}`);
2036    });
2037    gridEvent?.setOnReachStart(() => {
2038      console.info(`onReachStart`);
2039    });
2040    gridEvent?.setOnReachEnd(() => {
2041      console.info(`onReachEnd`);
2042    });
2043    gridEvent?.setOnScrollStart(() => {
2044      console.info(`onScrollStart`);
2045    });
2046    gridEvent?.setOnScrollStop(() => {
2047      console.info(`onScrollStop`);
2048    });
2049    gridEvent?.setOnScrollFrameBegin((offset: number, state: ScrollState) => {
2050      console.info(`onScrollFrameBegin offset = ${offset}, state = ${state}`);
2051      return undefined;
2052    });
2053    gridEvent?.setOnScrollIndex((first: number, last: number) => {
2054      console.info(`onScrollIndex start = ${first}, end = ${last}`);
2055    });
2056  }
2057}
2058
2059@Entry
2060@Component
2061struct Index {
2062  @State index: number = 0;
2063  private myNodeController: MyNodeController = new MyNodeController();
2064  @State numbers: string[] = [];
2065
2066  aboutToAppear() {
2067    for (let i = 0; i < 5; i++) {
2068      for (let j = 0; j < 5; j++) {
2069        this.numbers.push(j.toString());
2070      }
2071    }
2072  }
2073
2074  build() {
2075    Column() {
2076      Button("add CommonEvent to Grid")
2077        .onClick(() => {
2078          this.myNodeController!.addCommonEvent(this.myNodeController!.rootNode!.getParent()!.getPreviousSibling()!);
2079        })
2080      Grid() {
2081        ForEach(this.numbers, (day: string, index: number) => {
2082          GridItem() {
2083            Text(day)
2084              .fontSize(16)
2085              .backgroundColor(0xF9CF93)
2086              .width('100%')
2087              .height(80)
2088              .textAlign(TextAlign.Center)
2089          }
2090        }, (day: string, index: number) => index.toString() + day)
2091      }
2092      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
2093      .columnsGap(10)
2094      .rowsGap(10)
2095      .enableScrollInteraction(true)
2096      .width('90%')
2097      .backgroundColor(0xFAEEE0)
2098      .height(300)
2099      NodeContainer(this.myNodeController)
2100    }.width("100%")
2101  }
2102}
2103```
2104### 示例14(滚动到指定位置)
2105
2106该示例通过scrollToIndex接口,实现了Grid组件滚动到指定位置。
2107
2108GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
2109
2110<!--code_no_check-->
2111```ts
2112import { GridDataSource } from './GridDataSource';
2113@Entry
2114@Component
2115struct GridScrollToIndexSample {
2116  numbers: GridDataSource = new GridDataSource([]);
2117  scroller: Scroller = new Scroller();
2118  aboutToAppear(): void {
2119    let list: string[] = [];
2120    for (let i = 0; i < 10; i++) {
2121      for (let j = 0; j < 10; j++) {
2122        list.push((i * 5 + j  + 1).toString());
2123      }
2124    }
2125    this.numbers =  new GridDataSource(list);
2126  }
2127
2128  build() {
2129    Column({ space: 5 }) {
2130      Button('scrollToIndex')
2131        .onClick(() => { // 滚动到对应的位置
2132          this.scroller.scrollToIndex(25, true, ScrollAlign.START);
2133        })
2134      Grid(this.scroller) {
2135        LazyForEach(this.numbers, (day: string) => {
2136          GridItem() {
2137            Text(day)
2138              .fontSize(16)
2139              .backgroundColor(0xF9CF93)
2140              .width('100%')
2141              .height(80)
2142              .textAlign(TextAlign.Center)
2143          }
2144        }, (index: number) => index.toString())
2145      }
2146      .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
2147      .columnsGap(10)
2148      .rowsGap(10)
2149      .friction(0.6)
2150      .enableScrollInteraction(true)
2151      .supportAnimation(false)
2152      .multiSelectable(false)
2153      .edgeEffect(EdgeEffect.Spring)
2154      .scrollBar(BarState.On)
2155      .scrollBarColor(Color.Grey)
2156      .scrollBarWidth(4)
2157      .width('90%')
2158      .backgroundColor(0xFAEEE0)
2159      .height(300)
2160    }.width('100%').margin({ top: 5 })
2161  }
2162}
2163```
2164![grid_scrollToIndex](figures/gridScrollToIndex.gif)
2165
2166
2167### 示例15(实现Grid滑动选择)
2168
2169该示例通过[PanGesture](./ts-basic-gestures-pangesture.md#pangesture-1)接口,实现了Grid组件一边滑动一边选择的效果。
2170
2171GridDataSource说明及完整代码参考[示例2可滚动grid和滚动事件](#示例2可滚动grid和滚动事件)。
2172
2173<!--code_no_check-->
2174```ts
2175// xxx.ets
2176import { GridDataSource } from './GridDataSource';
2177import { display, curves } from '@kit.ArkUI';
2178
2179enum SlideActionType {
2180  START,
2181  UPDATE,
2182  END
2183}
2184// 热区
2185const HOT_AREA_LENGTH =
2186  Math.round(display.getDefaultDisplaySync().densityDPI * 10 / 25.4 / display.getDefaultDisplaySync().densityPixels);
2187// 滚动曲线: 贝塞尔曲线
2188const SLIDE_SELECT_SPEED_CURVE = curves.cubicBezierCurve(0.33, 0, 0.67, 1);
2189// 滚动速度: 最大速度
2190const AUTO_SPEED_MAX: number = Math.round(2400 / display.getDefaultDisplaySync().densityPixels);
2191@Entry
2192@Component
2193struct GridExample {
2194  numbers: GridDataSource = new GridDataSource([]);
2195  scroller: Scroller = new Scroller();
2196  @State selectedIndexes: string[] = [];
2197  // 滑动多选时,当前变更选中状态的item
2198  @State updateIndex: number = -1;
2199  @State lastUpdateIndex: number = -1;
2200  @State updateTimer: number = new Date().valueOf();
2201  // 是否可进行滑动多选
2202  @State canSlideSelect: boolean = false;
2203  @State isAutoScroll: boolean = false;
2204  // 停止手势
2205  @State stopGesture: boolean = false;
2206  private scrollStartIndex: number = 0;
2207  private scrollEndIndex: number = 0;
2208  // 滑动的初始点位
2209  @State startIndex: number = -1;
2210  @State endIndex: number = -1;
2211  // 滚动部位显示区域的高度
2212  @State contentHeight: number = 0;
2213  @State areaY: number = 0;
2214  // 列表宽度
2215  @State listWidth: number = 0;
2216  @State oldCheckList: boolean[] = [];
2217  // 滑动过程中是否将经过的点设为选中状态
2218  @State setChecked: boolean = false;
2219  aboutToAppear() {
2220    let list: string[] = [];
2221    for (let i = 0; i < 20; i++) {
2222      for (let j = 0; j < 20; j++) {
2223        list.push((20 * i + j + 1).toString());
2224      }
2225    }
2226    this.numbers = new GridDataSource(list);
2227  }
2228  /**
2229   * 获取当前点位
2230   * @param finger
2231   * @returns
2232   */
2233  getIndex(finger: FingerInfo): number {
2234    // 初始化数据
2235    let index = -1;
2236    try {
2237      index = this.scroller.getItemIndex(finger.localX, finger.localY);
2238      if (index === -1) {
2239        for (let i = this.scrollStartIndex; i <= this.scrollEndIndex; i++) {
2240          const item = this.scroller.getItemRect(i);
2241          if (finger.localY < item.y ||
2242            finger.localY >= item.y && finger.localY <= item.y + item.height && finger.localX < item.x) {
2243            break;
2244          }
2245          index = i;
2246        }
2247      }
2248    } catch {
2249      this.stopGesture = true;
2250      return index;
2251    }
2252    return index;
2253  }
2254  slideActionStart(index: number): void {
2255    if (index < 0) {
2256      return;
2257    }
2258    console.debug('start index: ' + index.toString());
2259    const targetIndex = index + 1;
2260    this.setChecked = !this.selectedIndexes.includes(targetIndex.toString());
2261    this.startIndex = index;
2262    this.selectedIndexes.push(targetIndex.toString());
2263    this.updateIndex = index;
2264
2265  }
2266  slideActionUpdate(index: number): void {
2267    if (!this.canSlideSelect) {
2268      return;
2269    }
2270    if (this.startIndex === -1) {
2271      //(初始接触点在空隙)时,重新配置滑动的初始数据
2272      this.slideActionStart(index);
2273      return;
2274    }
2275    if (index === -1) {
2276      return;
2277    }
2278
2279    this.lastUpdateIndex = this.updateIndex;
2280    this.setItemChecked(index);
2281    this.updateIndex = index;
2282  }
2283  setItemChecked(index: number):void {
2284    const start = Math.min(this.startIndex, index);
2285    const end = Math.max(this.startIndex, index);
2286    for (let i = start; i < end+1;i++) {
2287      const item = (i+1).toString();
2288      if (this.setChecked) {
2289        this.selectedIndexes.push(item);
2290      } else {
2291        if (this.selectedIndexes.includes(item)) {
2292          this.selectedIndexes = this.selectedIndexes.filter(selectIndex => selectIndex != item);
2293        }
2294      }
2295
2296    }
2297  }
2298  /**
2299   * 滑动结束
2300   */
2301  slideActionEnd(): void {
2302    this.startIndex = -1;
2303    this.updateIndex = -1;
2304    this.scroller.scrollBy(0, 0);
2305    this.isAutoScroll = false;
2306  }
2307  /**
2308   * 自动滚动--
2309   * @param finger
2310   */
2311  autoScroll(finger: FingerInfo): void {
2312    // 不可多选
2313    if (!this.canSlideSelect) {
2314      return;
2315    }
2316    let pointY = finger.globalY - this.areaY;
2317    if (pointY <= HOT_AREA_LENGTH) {
2318      if (this.isAutoScroll && pointY <= 0) {
2319        return;
2320      }
2321      const speedFlag = pointY > 0 ? SLIDE_SELECT_SPEED_CURVE
2322        .interpolate(1 - pointY / HOT_AREA_LENGTH) : 1;
2323      this.scroller.scrollEdge(Edge.Top, {
2324        velocity: speedFlag * AUTO_SPEED_MAX
2325      });
2326      this.isAutoScroll = true;
2327    } else if (pointY > this.contentHeight - HOT_AREA_LENGTH) {
2328      if (this.isAutoScroll && pointY >= this.contentHeight) {
2329        return;
2330      }
2331      const speedFlag = pointY < this.contentHeight ? SLIDE_SELECT_SPEED_CURVE
2332        .interpolate(1 - (this.contentHeight - pointY) / HOT_AREA_LENGTH) : 1;
2333      this.scroller.scrollEdge(Edge.Bottom, {
2334        velocity: speedFlag * AUTO_SPEED_MAX
2335      });
2336      this.isAutoScroll = true;
2337    } else {
2338      if (this.isAutoScroll) {
2339        this.scroller.scrollBy(0, 0);
2340        this.isAutoScroll = false;
2341      }
2342    }
2343  }
2344
2345  panGestureAction(type: SlideActionType, event: GestureEvent | undefined): void {
2346    if (this.stopGesture || !event) {
2347      return;
2348    }
2349    const finger = event!.fingerList[0];
2350    const index = this.getIndex(finger);
2351    switch (type) {
2352      case SlideActionType.START: {
2353        this.slideActionStart(index);
2354        break;
2355      }
2356      case SlideActionType.UPDATE: {
2357        this.slideActionUpdate(index);
2358        this.autoScroll(finger);
2359        break;
2360      }
2361      case SlideActionType.END: {
2362        this.slideActionEnd();
2363        break;
2364      }
2365      default: {
2366      }
2367    }
2368  }
2369  build() {
2370    Column({ space: 5 }) {
2371      Grid(this.scroller) {
2372        LazyForEach(this.numbers, (day: string) => {
2373          GridItem() {
2374            Stack() {
2375              Text(day)
2376                .fontSize(16)
2377                .backgroundColor(0xF9CF93)
2378                .width('100%')
2379                .height(80)
2380                .textAlign(TextAlign.Center)
2381              if (this.canSlideSelect) {
2382                // $r('app.media.gouxuan')和$r('app.media.weigouxuan')需要替换为开发者所需的图像资源文件。
2383                Image(this.selectedIndexes.includes(day) ? $r('app.media.gouxuan') :$r('app.media.weigouxuan'));
2384                  .width(30)
2385                  .height(30)
2386                  .position({right:5,top:5})
2387                  .draggable(false)
2388              }
2389
2390            }
2391          }
2392        }, (index: number) => index.toString())
2393      }
2394      .columnsTemplate('1fr 1fr 1fr')
2395      .columnsGap(10)
2396      .rowsGap(10)
2397      .friction(0.6)
2398      .enableScrollInteraction(true)
2399      .supportAnimation(false)
2400      .multiSelectable(false)
2401      .edgeEffect(EdgeEffect.Spring)
2402      .scrollBar(BarState.On)
2403      .scrollBarColor(Color.Grey)
2404      .scrollBarWidth(4)
2405      .width('90%')
2406      .height('85%')
2407      .draggable(!this.canSlideSelect)
2408      .backgroundColor(0xFAEEE0)
2409      .onAreaChange((oldVal, newVal) => {
2410        this.listWidth = newVal.width as number;
2411        this.areaY = newVal.globalPosition.y as number;
2412        this.contentHeight = newVal.height as number;
2413      })
2414      .onScrollIndex((start, end) => {
2415        this.scrollStartIndex = start;
2416        this.scrollEndIndex = end;
2417      })
2418      .gesture(
2419        // 手势滑动
2420        PanGesture({ direction: PanDirection.Vertical })
2421          .onActionStart((event: GestureEvent | undefined) => {
2422            this.panGestureAction(SlideActionType.START, event);
2423          })
2424          .onActionUpdate((event: GestureEvent | undefined) => {
2425            this.panGestureAction(SlideActionType.UPDATE, event);
2426          })
2427          .onActionEnd((event?: GestureEvent) => {
2428            this.panGestureAction(SlideActionType.END, event);
2429          }),
2430        GestureMask.Normal
2431      )
2432      .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer,
2433        recognizers: Array<GestureRecognizer>) => {
2434        if (this.canSlideSelect && current.isBuiltIn() &&
2435          current.getType() == GestureControl.GestureType.PAN_GESTURE) {
2436          return GestureJudgeResult.REJECT;
2437        }
2438        return GestureJudgeResult.CONTINUE;
2439      })
2440      Row() {
2441        Button('开始编辑').onClick(()=>{
2442          this.selectedIndexes = [];
2443          this.canSlideSelect = true;
2444        })
2445        Button('结束编辑').onClick(()=>{
2446          this.canSlideSelect = false;
2447          this.selectedIndexes = [];
2448        })
2449      }
2450      .margin({
2451        bottom: 30
2452      })
2453      Text(`${this.selectedIndexes.join(',')}`)
2454    }.width('100%').margin({ top: 5 })
2455  }
2456}
2457```
2458
2459![gridScrollWithPanGesture](figures/gridScrollWithPanGesture.gif)
2460
2461### 示例16(实现GridItem自定义拖拽)
2462
2463该示例通过[gesture](./ts-gesture-settings.md#gesture)接口,实现了GridItem组件自定义拖拽效果。
2464
2465```ts
2466import { curves } from '@kit.ArkUI';
2467
2468@Entry
2469@Component
2470struct GridItemExample {
2471  @State numbers: number[] = [];
2472  @State dragItem: number = -1;
2473  @State scaleItem: number = -1;
2474  @State item: number = -1;
2475  private dragRefOffsetX: number = 0;
2476  private dragRefOffsetY: number = 0;
2477  @State offsetX: number = 0;
2478  @State offsetY: number = 0;
2479  private FIX_VP_X: number = 108;
2480  private FIX_VP_Y: number = 120;
2481
2482  aboutToAppear() {
2483    for (let i = 1; i <= 11; i++) {
2484      this.numbers.push(i);
2485    }
2486  }
2487
2488  itemMove(index: number, newIndex: number): void {
2489    console.info('index:' + index + ' newIndex:' + newIndex);
2490    if (!this.isDraggable(newIndex)) {
2491      return;
2492    }
2493    let tmp = this.numbers.splice(index, 1);
2494    this.numbers.splice(newIndex, 0, tmp[0]);
2495  }
2496
2497  //向下滑
2498  down(index: number): void {
2499    // 指定固定GridItem不响应事件
2500    if (!this.isDraggable(index + 3)) {
2501      return;
2502    }
2503    this.offsetY -= this.FIX_VP_Y;
2504    this.dragRefOffsetY += this.FIX_VP_Y;
2505    this.itemMove(index, index + 3);
2506  }
2507
2508  //向下滑(右下角为空)
2509  down2(index: number): void {
2510    if (!this.isDraggable(index + 3)) {
2511      return;
2512    }
2513    this.offsetY -= this.FIX_VP_Y;
2514    this.dragRefOffsetY += this.FIX_VP_Y;
2515    this.itemMove(index, index + 3);
2516  }
2517
2518  //向上滑
2519  up(index: number): void {
2520    if (!this.isDraggable(index - 3)) {
2521      return;
2522    }
2523    this.offsetY += this.FIX_VP_Y;
2524    this.dragRefOffsetY -= this.FIX_VP_Y;
2525    this.itemMove(index, index - 3);
2526  }
2527
2528  //向左滑
2529  left(index: number): void {
2530    if (!this.isDraggable(index - 1)) {
2531      return;
2532    }
2533    this.offsetX += this.FIX_VP_X;
2534    this.dragRefOffsetX -= this.FIX_VP_X;
2535    this.itemMove(index, index - 1);
2536  }
2537
2538  //向右滑
2539  right(index: number): void {
2540    if (!this.isDraggable(index + 1)) {
2541      return;
2542    }
2543    this.offsetX -= this.FIX_VP_X;
2544    this.dragRefOffsetX += this.FIX_VP_X;
2545    this.itemMove(index, index + 1);
2546  }
2547
2548  //向右下滑
2549  lowerRight(index: number): void {
2550    if (!this.isDraggable(index + 4)) {
2551      return;
2552    }
2553    this.offsetX -= this.FIX_VP_X;
2554    this.dragRefOffsetX += this.FIX_VP_X;
2555    this.offsetY -= this.FIX_VP_Y;
2556    this.dragRefOffsetY += this.FIX_VP_Y;
2557    this.itemMove(index, index + 4);
2558  }
2559
2560  //向右上滑
2561  upperRight(index: number): void {
2562    if (!this.isDraggable(index - 2)) {
2563      return;
2564    }
2565    this.offsetX -= this.FIX_VP_X;
2566    this.dragRefOffsetX += this.FIX_VP_X;
2567    this.offsetY += this.FIX_VP_Y;
2568    this.dragRefOffsetY -= this.FIX_VP_Y;
2569    this.itemMove(index, index - 2);
2570  }
2571
2572  //向左下滑
2573  lowerLeft(index: number): void {
2574    if (!this.isDraggable(index + 2)) {
2575      return;
2576    }
2577    this.offsetX += this.FIX_VP_X;
2578    this.dragRefOffsetX -= this.FIX_VP_X;
2579    this.offsetY -= this.FIX_VP_Y;
2580    this.dragRefOffsetY += this.FIX_VP_Y;
2581    this.itemMove(index, index + 2);
2582  }
2583
2584  //向左上滑
2585  upperLeft(index: number): void {
2586    if (!this.isDraggable(index - 4)) {
2587      return;
2588    }
2589    this.offsetX += this.FIX_VP_X;
2590    this.dragRefOffsetX -= this.FIX_VP_X;
2591    this.offsetY += this.FIX_VP_Y;
2592    this.dragRefOffsetY -= this.FIX_VP_Y;
2593    this.itemMove(index, index - 4);
2594  }
2595
2596  isDraggable(index: number): boolean {
2597    console.info('index:' + index)
2598    return index > 1;
2599  }
2600
2601  build() {
2602    Column() {
2603      Grid() {
2604        ForEach(this.numbers, (item: number) => {
2605          GridItem() {
2606            Text(item + '')
2607              .fontSize(16)
2608              .width('100%')
2609              .textAlign(TextAlign.Center)
2610              .height(100)
2611              .borderRadius(10)
2612              .backgroundColor(0xF9CF93)
2613              .shadow(this.scaleItem == item ? {
2614                radius: 70,
2615                color: '#15000000',
2616                offsetX: 0,
2617                offsetY: 0
2618              } :
2619                {
2620                  radius: 0,
2621                  color: '#15000000',
2622                  offsetX: 0,
2623                  offsetY: 0
2624                })
2625              .animation({ curve: Curve.Sharp, duration: 300 })
2626          }
2627          // 指定固定GridItem不响应事件
2628          .hitTestBehavior(this.isDraggable(this.numbers.indexOf(item)) ? HitTestMode.Default : HitTestMode.None)
2629          .scale({ x: this.scaleItem == item ? 1.05 : 1, y: this.scaleItem == item ? 1.05 : 1 })
2630          .zIndex(this.dragItem == item ? 1 : 0)
2631          .translate(this.dragItem == item ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 })
2632          .padding(10)
2633          .gesture(
2634            // 以下组合手势为顺序识别,当长按手势事件未正常触发时则不会触发拖动手势事件
2635            GestureGroup(GestureMode.Sequence,
2636              LongPressGesture({ repeat: true })
2637                .onAction((event?: GestureEvent) => {
2638                  this.getUIContext()?.animateTo({ curve: Curve.Friction, duration: 300 }, () => {
2639                    this.scaleItem = item;
2640                  })
2641                })
2642                .onActionEnd(() => {
2643                  this.getUIContext()?.animateTo({ curve: Curve.Friction, duration: 300 }, () => {
2644                    this.scaleItem = -1;
2645                  })
2646                }),
2647              PanGesture({ fingers: 1, direction: null, distance: 0 })
2648                .onActionStart(() => {
2649                  this.dragItem = item;
2650                  this.dragRefOffsetX = 0;
2651                  this.dragRefOffsetY = 0;
2652                })
2653                .onActionUpdate((event: GestureEvent) => {
2654                  this.offsetY = event.offsetY - this.dragRefOffsetY;
2655                  this.offsetX = event.offsetX - this.dragRefOffsetX;
2656                  this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
2657                    let index = this.numbers.indexOf(this.dragItem);
2658                    if (this.offsetY >= this.FIX_VP_Y / 2 && (this.offsetX <= 44 && this.offsetX >= -44) &&
2659                      ![8, 9, 10].includes(index)) {
2660                      //向下滑
2661                      this.down(index);
2662                    } else if (this.offsetY <= -this.FIX_VP_Y / 2 && (this.offsetX <= 44 && this.offsetX >= -44) &&
2663                      ![0, 1, 2].includes(index)) {
2664                      //向上滑
2665                      this.up(index);
2666                    } else if (this.offsetX >= this.FIX_VP_X / 2 && (this.offsetY <= 50 && this.offsetY >= -50) &&
2667                      ![2, 5, 8, 10].includes(index)) {
2668                      //向右滑
2669                      this.right(index);
2670                    } else if (this.offsetX <= -this.FIX_VP_X / 2 && (this.offsetY <= 50 && this.offsetY >= -50) &&
2671                      ![0, 3, 6, 9].includes(index)) {
2672                      //向左滑
2673                      this.left(index);
2674                    } else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2 &&
2675                      ![2, 5, 7, 8, 9, 10].includes(index)) {
2676                      //向右下滑
2677                      this.lowerRight(index);
2678                    } else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2 &&
2679                      ![0, 1, 2, 5, 8].includes(index)) {
2680                      //向右上滑
2681                      this.upperRight(index);
2682                    } else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2 &&
2683                      ![0, 3, 6, 9, 10].includes(index)) {
2684                      //向左下滑
2685                      this.lowerLeft(index);
2686                    } else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2 &&
2687                      ![0, 1, 2, 3, 6, 9].includes(index)) {
2688                      //向左上滑
2689                      this.upperLeft(index);
2690                    } else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2 &&
2691                    [7].includes(index)) {
2692                      //向右下滑(右下角为空)
2693                      this.down2(index);
2694                    }
2695                  })
2696                })
2697                .onActionEnd(() => {
2698                  this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
2699                    this.dragItem = -1;
2700                  })
2701                  this.getUIContext()?.animateTo({
2702                    curve: curves.interpolatingSpring(14, 1, 170, 17), delay: 150
2703                  }, () => {
2704                    this.scaleItem = -1;
2705                  })
2706                })
2707            )
2708              .onCancel(() => {
2709                this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
2710                  this.dragItem = -1;
2711                })
2712                this.getUIContext()?.animateTo({
2713                  curve: curves.interpolatingSpring(14, 1, 170, 17)
2714                }, () => {
2715                  this.scaleItem = -1;
2716                })
2717              })
2718          )
2719        }, (item: number) => item.toString())
2720      }
2721      .width('90%')
2722      .editMode(true)
2723      .scrollBar(BarState.Off)
2724      .columnsTemplate('1fr 1fr 1fr')
2725    }.width('100%').height('100%').backgroundColor('#0D182431').padding({ top: 5 })
2726  }
2727}
2728```
2729
2730![gridCustomDrag](figures/gridCustomDrag.gif)