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>  86 87### columnsTemplate 88 89columnsTemplate(value: string) 90 91设置当前网格布局列的数量、固定列宽或最小列宽值,不设置时默认1列。 92 93例如, '1fr 1fr 2fr' 是将父组件分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例如, '1fr 1fr 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) \| number \| 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 \| 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 \| [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) \| 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) \| 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) \| 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 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 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 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 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 1524 1525网格子组件拖拽过程中: 1526 1527 1528 1529网格子组件1与子组件6拖拽交换位置后: 1530 1531 1532 1533拖拽动画: 1534 1535 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 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 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 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 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 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 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 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 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 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