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