鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Grid)
网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。
说明:
该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
子组件
仅支持GridItem子组件。
说明:
Grid子组件的索引值计算规则:
按子组件的顺序依次递增。
if/else语句中,只有条件成立分支内的子组件会参与索引值计算,条件不成立分支内的子组件不计算索引值。
ForEach/LazyForEach语句中,会计算展开所有子节点索引值。
if/else、ForEach和LazyForEach发生变化以后,会更新子节点索引值。
Grid子组件的visibility属性设置为Hidden或None时依然会计算索引值。
Grid子组件的visibility属性设置为None时不显示,但依然会占用子组件对应的网格。
Grid子组件设置position属性,会占用子组件对应的网格,子组件将显示在相对Grid左上角偏移position的位置。该子组件不会随其对应网格滚动,在对应网格滑出Grid显示范围外后不显示。
当Grid子组件之间留有空隙时,会根据当前的展示区域尽可能填补空隙,因此GridItem可能会随着网格滚动而改变相对位置。
接口
Grid(scroller?: Scroller, layoutOptions?: GridLayoutOptions)
参数:
参数名 | 参数类型 | 必填 | 参数描述 |
---|---|---|---|
scroller | Scroller | 否 | 可滚动组件的控制器,与可滚动组件绑定。 说明: 不允许和其他滚动类组件,如:List、Grid、Scroll等绑定同一个滚动控制对象。 |
layoutOptions10+ | GridLayoutOptions | 否 | 滚动Grid布局选项。 |
GridLayoutOptions10+
布局选项。其中,irregularIndexes和onGetIrregularSizeByIndex可对仅设置rowsTemplate或columnsTemplate的Grid使用,可以指定一个index数组,并为其中的index对应的GridItem设置其占据的行数与列数,使用方法参见示例3;onGetRectByIndex可对同时设置rowsTemplate和columnsTemplate的Grid使用,为指定的index对应的GridItem设置位置和大小,使用方法参见示例1。
参数:
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
regularSize | [number, number] | 是 | 大小规则的GridItem在Grid中占的行数和列数,只支持占1行1列即[1, 1]。 |
irregularIndexes | number[] | 否 | 指定的GridItem索引在Grid中的大小是不规则的。当不设置onGetIrregularSizeByIndex时,irregularIndexes中GridItem的默认大小为垂直滚动Grid的一整行或水平滚动Grid的一整列。 |
onGetIrregularSizeByIndex | (index: number) => [number, number] | 否 | 配合irregularIndexes使用,设置不规则GridItem占用的行数和列数。开发者可为irregularIndexes中指明的index对应的GridItem设置占用的行数和列数。垂直滚动Grid不支持GridItem占多行,水平滚动Grid不支持GridItem占多列。 |
onGetRectByIndex11+ | (index: number) => [number, number,number,number] | 否 | 设置指定索引index对应的GridItem的位置及大小[rowStart,columnStart,rowSpan,columnSpan]。 其中rowStart为行起始位置,columnStart为列起始位置,无单位。 rowSpan为GridItem占用的行数,columnSpan为GridItem占用的列数,无单位。 rowStart和columnStart取大于等于0的自然数,若取负数时,rowStart和columnStart默认为0。 rowSpan和columnSpan取大于等于1的自然数,若取小数则向下取整,若小于1则按1计算。 说明: 第一种情况:某个GridItem发现给它指定的起始位置被占据了,则从起始位置[0,0]开始按顺序从左到右,从上到下寻找起始的放置位置。 第二种情况:如果起始位置没有被占据,但其他位置被占据了,无法显示全部的GridItem大小,则只会布局一部分。 |
属性
除支持通用属性外,还支持以下属性:
名称 | 参数类型 | 描述 |
---|---|---|
columnsTemplate | string | 设置当前网格布局列的数量或最小列宽值,不设置时默认1列。 例如, '1fr 1fr 2fr' 是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。 columnsTemplate('repeat(auto-fit, track-size)')是设置最小列宽值为track-size,自动计算列数和实际列宽。 columnsTemplate('repeat(auto-fill, track-size)')是设置固定列宽值为track-size,自动计算列数。 其中repeat、auto-fit、auto-fill为关键字。track-size为列宽,支持的单位包括px、vp、%或有效数字,默认单位为vp,track-size至少包括一个有效列宽。 说明: 设置为'0fr'时,该列的列宽为0,不显示GridItem。设置为其他非法值时,GridItem显示为固定1列。 |
rowsTemplate | string | 设置当前网格布局行的数量或最小行高值,不设置时默认1行。 例如, '1fr 1fr 2fr'是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。 rowsTemplate('repeat(auto-fit, track-size)')是设置最小行高值为track-size,自动计算行数和实际行高。 rowsTemplate('repeat(auto-fill, track-size)')是设置固定行高值为track-size,自动计算行数。 其中repeat、auto-fit、auto-fill为关键字。track-size为行高,支持的单位包括px、vp、%或有效数字,默认单位为vp,track-size至少包括一个有效行高。 说明: 设置为'0fr',则这一行的行宽为0,这一行GridItem不显示。设置为其他非法值,按固定1行处理。 |
columnsGap | Length | 设置列与列的间距。 默认值:0 说明: 设置为小于0的值时,按默认值显示。 |
rowsGap | Length | 设置行与行的间距。 默认值:0 说明: 设置为小于0的值时,按默认值显示。 |
scrollBar | BarState | 设置滚动条状态。 默认值:BarState.Off 说明: API version 9及以下版本默认值为BarState.Off,API version 10的默认值为BarState.Auto。 |
scrollBarColor | string | number | Color | 设置滚动条的颜色。 |
scrollBarWidth | string | number | 设置滚动条的宽度。宽度设置后,滚动条正常状态和按压状态宽度均为滚动条的宽度值。 默认值:4 单位:vp |
cachedCount | number | 设置预加载的GridItem的数量,只在LazyForEach中生效。具体使用可参考减少应用白块说明。 默认值:1 说明: 设置缓存后会在Grid显示区域上下各缓存cachedCount*列数个GridItem。 LazyForEach超出显示和缓存范围的GridItem会被释放。 设置为小于0的值时,按默认值显示。 |
editMode 8+ | boolean | 设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem。 默认值:flase |
layoutDirection8+ | GridDirection | 设置布局的主轴方向。 默认值:GridDirection.Row |
maxCount8+ | number | 当layoutDirection是Row/RowReverse时,表示可显示的最大列数 当layoutDirection是Column/ColumnReverse时,表示可显示的最大行数。 默认值:Infinity 说明: 当maxCount小于minCount时,maxCount和minCount都按默认值处理。 设置为小于1的值时,按默认值显示。 |
minCount8+ | number | 当layoutDirection是Row/RowReverse时,表示可显示的最小列数。 当layoutDirection是Column/ColumnReverse时,表示可显示的最小行数。 默认值:1 说明: 设置为小于1的值时,按默认值显示。 |
cellLength8+ | number | 当layoutDirection是Row/RowReverse时,表示一行的高度。 当layoutDirection是Column/ColumnReverse时,表示一列的宽度。 默认值:第一个元素的大小 |
multiSelectable8+ | boolean | 是否开启鼠标框选。 默认值:false - false:关闭框选。 - true:开启框选。 说明: 开启框选后,可以配合Griditem的selected属性和onSelect事件获取GridItem的选中状态,还可以设置选中态样式(无默认选中样式)。 |
supportAnimation8+ | boolean | 是否支持动画。当前支持GridItem拖拽动画。 默认值:false 说明: 仅在滚动模式下(只设置rowsTemplate、columnsTemplate其中一个)支持动画。 |
edgeEffect10+ | value:EdgeEffect, options?:EdgeEffectOptions11+ | 设置边缘滑动效果。 - value: 设置Grid组件的边缘滑动效果,支持弹簧效果和阴影效果。 默认值:EdgeEffect.None - options:设置组件内容大小小于组件自身时,是否开启滑动效果。 默认值:false |
enableScrollInteraction10+ | boolean | 设置是否支持滚动手势,当设置为false时,无法通过手指或者鼠标滚动,但不影响控制器的滚动接口。 默认值:true |
nestedScroll10+ | NestedScrollOptions | 嵌套滚动选项。设置向前向后两个方向上的嵌套滚动模式,实现与父组件的滚动联动。 |
friction10+ | number | Resource | 设置摩擦系数,手动划动滚动区域时生效,只对惯性滚动过程有影响,对惯性滚动过程中的链式效果有间接影响。 默认值:非可穿戴设备为0.6,可穿戴设备为0.9 说明: 设置为小于等于0的值时,按默认值处理 |
Grid组件根据rowsTemplate、columnsTemplate属性的设置情况,可分为以下三种布局模式:
1、rowsTemplate、columnsTemplate同时设置:
- Grid只展示固定行列数的元素,其余元素不展示,且Grid不可滚动。
- 此模式下以下属性不生效:layoutDirection、maxCount、minCount、cellLength。
- Grid的宽高没有设置时,默认适应父组件尺寸。
- Gird网格列大小按照Gird自身内容区域大小减去所有行列Gap后按各个行列所占比重分配。
- GridItem默认填满网格大小。
2、rowsTemplate、columnsTemplate仅设置其中的一个:
- 元素按照设置的方向进行排布,超出Grid显示区域后,Grid可通过滚动的方式展示。
- 如果设置了columnsTemplate,Gird滚动方向为垂直方向,主轴方向为垂直方向,交叉轴方向为水平方向。
- 如果设置了rowsTemplate,Gird滚动方向为水平方向,主轴方向为水平方向,交叉轴方向为垂直方向。
- 此模式下以下属性不生效:layoutDirection、maxCount、minCount、cellLength。
- 网格交叉轴方向尺寸根据Gird自身内容区域交叉轴尺寸减去交叉轴方向所有Gap后按所占比重分配。
- 网格主轴方向尺寸取当前网格交叉轴方向所有GridItem高度最大值。
3、rowsTemplate、columnsTemplate都不设置:
- 元素在layoutDirection方向上排布,列数由Grid的宽度、首个元素的宽度、minCount、maxCount、columnsGap共同决定。
- 行数由Grid高度、首个元素高度、cellLength、rowsGap共同决定。超出行列容纳范围的元素不显示,也不能通过滚动进行展示。
- 此模式下仅生效以下属性:layoutDirection、maxCount、minCount、cellLength、editMode、columnsGap、rowsGap。
- 当前layoutDirection设置为Row时,先从左到右排列,排满一行再排下一行。剩余高度不足时不再布局,整体内容顶部居中。
- 当前layoutDirection设置为Column时,先从上到下排列,排满一列再排下一列,剩余宽度不足时不再布局。整体内容顶部居中。
GridDirection8+枚举说明
名称 | 枚举值 | 描述 |
---|---|---|
Row | 0 | 主轴布局方向沿水平方向布局,即自左往右先填满一行,再去填下一行。 |
Column | 1 | 主轴布局方向沿垂直方向布局,即自上往下先填满一列,再去填下一列。 |
RowReverse | 2 | 主轴布局方向沿水平方向反向布局,即自右往左先填满一行,再去填下一行。 |
ColumnReverse | 3 | 主轴布局方向沿垂直方向反向布局,即自下往上先填满一列,再去填下一列。 |
说明:
Grid组件通用属性clip的默认值为true。
事件
除支持通用事件外,还支持以下事件:
名称 | 功能描述 |
---|---|
onScrollIndex(event: (first: number, last10+: number) => void) | 当前网格显示的起始位置/终止位置的item发生变化时触发。网格初始化时会触发一次。 - first: 当前显示的网格起始位置的索引值。 - last: 当前显示的网格终止位置的索引值。 Grid显示区域上第一个子组件/最后一个组件的索引值有变化就会触发。 |
onItemDragStart8+(event: (event: ItemDragInfo, itemIndex: number) => (() => any) | void) | 开始拖拽网格元素时触发。 - event: 见ItemDragInfo对象说明。 - itemIndex: 被拖拽网格元素索引值。 说明: 返回void表示不能拖拽。 手指长按GridItem时触发该事件。 由于拖拽检测也需要长按,且事件处理机制优先触发子组件事件,GridItem上绑定LongPressGesture时无法触发拖拽;如有长按和拖拽同时使用的需求可以使用通用拖拽事件。 |
onItemDragEnter8+(event: (event: ItemDragInfo) => void) | 拖拽进入网格元素范围内时触发。 - event: 见ItemDragInfo对象说明。 |
onItemDragMove8+(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number) => void) | 拖拽在网格元素范围内移动时触发。 - event: 见ItemDragInfo对象说明。 - itemIndex: 拖拽起始位置。 - insertIndex: 拖拽插入位置。 |
onItemDragLeave8+(event: (event: ItemDragInfo, itemIndex: number) => void) | 拖拽离开网格元素时触发。 - event: 见ItemDragInfo对象说明。 - itemIndex: 拖拽离开的网格元素索引值。 |
onItemDrop8+(event: (event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => void) | 绑定该事件的网格元素可作为拖拽释放目标,当在网格元素内停止拖拽时触发。 - event: 见ItemDragInfo对象说明。 - itemIndex: 拖拽起始位置。 - insertIndex: 拖拽插入位置。 - isSuccess: 是否成功释放。 |
onScrollBarUpdate10+(event: (index: number, offset: number) => ComputedBarAttribute) | 当前网格显示的起始位置item发生变化时触发,可通过该回调设置滚动条的位置及长度。 - index: 当前显示的网格起始位置的索引值。 - offset: 当前显示的网格起始位置元素相对网格显示起始位置的偏移,单位vp。 - ComputedBarAttribute: 见ComputedBarAttribute对象说明。 |
onScroll10+(event: (scrollOffset: number, scrollState: ScrollState) => void) | 网格滑动时触发。 - scrollOffset: 每帧滚动的偏移量,Grid的内容向上滚动时偏移量为正,向下滚动时偏移量为负,单位vp。 - scrollState: 当前滑动状态。 |
onReachStart10+(event: () => void) | 网格到达起始位置时触发。 说明: Grid初始化时会触发一次,Grid滚动到起始位置时触发一次。Grid边缘效果为弹簧效果时,划动经过起始位置时触发一次,回弹回起始位置时再触发一次。 |
onReachEnd10+(event: () => void) | 网格到达末尾位置时触发。 说明: Grid边缘效果为弹簧效果时,划动经过末尾位置时触发一次,回弹回末尾位置时再触发一次。 |
onScrollFrameBegin10+(event: (offset: number, state: ScrollState) => { offsetRemain: number }) | 网格开始滑动时触发,事件参数传入即将发生的滑动量,事件处理函数中可根据应用场景计算实际需要的滑动量并作为事件处理函数的返回值返回,网格将按照返回值的实际滑动量进行滑动。 - offset:即将发生的滑动量,单位vp。 - state:当前滑动状态。 - offsetRemain:实际滑动量,单位vp。 触发该事件的条件:手指拖动Grid、Grid惯性划动时每帧开始时触发;Grid超出边缘回弹、使用滚动控制器和拖动滚动条的滚动不会触发。 说明: 当gridDirection的值为Axis.Vertical时,返回垂直方向滑动量,当gridDirection的值为Axis.Horizontal时,返回水平方向滑动量。 |
onScrollStart10+(event: () => void) | 网格滑动开始时触发。手指拖动网格或网格的滚动条触发的滑动开始时,会触发该事件。使用Scroller滑动控制器触发的带动画的滑动,动画开始时会触发该事件。 |
onScrollStop10+(event: () => void) | 网格滑动停止时触发。手指拖动网格或网格的滚动条触发的滑动,手指离开屏幕并且滑动停止时会触发该事件;使用Scroller滑动控制器触发的带动画的滑动,动画停止会触发该事件。 |
ItemDragInfo对象说明
名称 | 类型 | 描述 |
---|---|---|
x | number | 当前拖拽点的x坐标,单位vp。 |
y | number | 当前拖拽点的y坐标,单位vp。 |
ComputedBarAttribute对象说明
名称 | 类型 | 描述 |
---|---|---|
totalOffset | number | Grid内容相对显示区域的总偏移,单位px。 |
totalLength | number | Grid内容总长度,单位px。 |
示例
示例1
固定行列的Grid,可以使用GridLayoutOptions中的onGetRectByIndex指定GridItem的位置和大小。
// xxx.ets
@Entry
@Component
struct GridExample {@State numbers1: String[] = ['0', '1', '2', '3', '4']@State numbers2: String[] = ['0', '1','2','3','4','5']layoutOptions3: GridLayoutOptions = {regularSize: [1, 1],onGetRectByIndex: (index: number) => {if (index == 0)return [0, 0, 1, 1]else if(index==1)return [0, 1, 2, 2]else if(index==2)return [0 ,3 ,3 ,3]else if(index==3)return [3, 0, 3, 3]else if(index==4)return [4, 3, 2, 2]elsereturn [5, 5, 1, 1]}}build() {Column({ space: 5 }) {Grid() {ForEach(this.numbers1, (day: string) => {ForEach(this.numbers1, (day: string) => {GridItem() {Text(day).fontSize(16).backgroundColor(0xF9CF93).width('100%').height('100%').textAlign(TextAlign.Center)}}, (day: string) => day)}, (day: string) => day)}.columnsTemplate('1fr 1fr 1fr 1fr 1fr').rowsTemplate('1fr 1fr 1fr 1fr 1fr').columnsGap(10).rowsGap(10).width('90%').backgroundColor(0xFAEEE0).height(300)Text('GridLayoutOptions的使用:onGetRectByIndex。').fontColor(0xCCCCCC).fontSize(9).width('90%')Grid(undefined, this.layoutOptions3) {ForEach(this.numbers2, (day: string) => {GridItem() {Text(day).fontSize(16).backgroundColor(0xF9CF93).width('100%').height("100%").textAlign(TextAlign.Center)}.height("100%").width('100%')}, (day: string) => day)}.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr').rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr').columnsGap(10).rowsGap(10).width('90%').backgroundColor(0xFAEEE0).height(300)}.width('100%').margin({ top: 5 })}
}
示例2
可滚动Grid,包括所有滚动属性和事件。
// xxx.ets
@Entry
@Component
struct GridExample {@State numbers: String[] = ['0', '1', '2', '3', '4']scroller: Scroller = new Scroller()@State Position: number = 0 //0代表滚动到grid顶部,1代表中间值,2代表滚动到grid底部。build() {Column({ space: 5 }) {Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')Grid(this.scroller) {ForEach(this.numbers, (day: string) => {ForEach(this.numbers, (day: string) => {GridItem() {Text(day).fontSize(16).backgroundColor(0xF9CF93).width('100%').height(80).textAlign(TextAlign.Center)}}, (day: string) => day)}, (day: string) => day)}.columnsTemplate('1fr 1fr 1fr 1fr 1fr').columnsGap(10).rowsGap(10).friction(0.6).enableScrollInteraction(true).supportAnimation(false).multiSelectable(false).edgeEffect(EdgeEffect.Spring).scrollBar(BarState.On).scrollBarColor(Color.Grey).scrollBarWidth(4).width('90%').backgroundColor(0xFAEEE0).height(300).onScrollIndex((first: number) => {console.info(first.toString())}).onScrollBarUpdate((index: number, offset: number) => {console.info("XXX" + 'Grid onScrollBarUpdate,index : ' + index.toString() + ",offset" + offset.toString())return { totalOffset: (index / 5) * (80 + 10) - offset, totalLength: 80 * 5 + 10 * 4 }}) //只适用于当前示例代码数据源,如果数据源有变化,则需要修改该部分代码,或者删掉此属性.onScroll((first: number, last: number) => {console.info(first.toString())console.info(last.toString())}).onScrollStart(() => {console.info("XXX" + "Grid onScrollStart")}).onScrollStop(() => {console.info("XXX" + "Grid onScrollStop")}).onReachStart(() => {this.Position = 0console.info("XXX" + "Grid onReachStart")}).onReachEnd(() => {this.Position = 2console.info("XXX" + "Grid onReachEnd")})Button('next page').onClick(() => { // 点击后滑到下一页this.scroller.scrollPage({ next: true })})}.width('100%').margin({ top: 5 })}
}
示例3
GridLayoutOptions的使用:irregularIndexes与onGetIrregularSizeByIndex。
// xxx.ets
@Entry
@Component
struct GridExample {@State numbers: String[] = ['0', '1', '2', '3', '4']scroller: Scroller = new Scroller()layoutOptions1: GridLayoutOptions = {regularSize: [1, 1], // 只支持[1, 1]irregularIndexes: [0, 6], // 索引为0和6的GridItem占用一行}layoutOptions2: GridLayoutOptions = {regularSize: [1, 1],irregularIndexes: [0, 7], // 索引为0和7的GridItem占用的列数由onGetIrregularSizeByIndex指定onGetIrregularSizeByIndex: (index: number) => {if (index === 0) {return [1, 5]}return [1, index % 6 + 1]}}build() {Column({ space: 5 }) {Grid(this.scroller, this.layoutOptions1) {ForEach(this.numbers, (day: string) => {ForEach(this.numbers, (day: string) => {GridItem() {Text(day).fontSize(16).backgroundColor(0xF9CF93).width('100%').height(80).textAlign(TextAlign.Center)}.selectable(false)}, (day: string) => day)}, (day: string) => day)}.columnsTemplate('1fr 1fr 1fr 1fr 1fr').columnsGap(10).rowsGap(10).multiSelectable(true).scrollBar(BarState.Off).width('90%').backgroundColor(0xFAEEE0).height(300)Text('scroll').fontColor(0xCCCCCC).fontSize(9).width('90%')// 不使用scroll,需要undefined占位Grid(undefined, this.layoutOptions2) {ForEach(this.numbers, (day: string) => {ForEach(this.numbers, (day: string) => {GridItem() {Text(day).fontSize(16).backgroundColor(0xF9CF93).width('100%').height(80).textAlign(TextAlign.Center)}}, (day: string) => day)}, (day: string) => day)}.columnsTemplate('1fr 1fr 1fr 1fr 1fr').columnsGap(10).rowsGap(10).scrollBar(BarState.Off).width('90%').backgroundColor(0xFAEEE0).height(300)}.width('100%').margin({ top: 5 })}
}
示例4
nestedScroll和onScrollFrameBegin的使用。
@Entry
@Component
struct GridExample {@State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]@State numbers: number[] = []@State translateY: number = 0private scroller: Scroller = new Scroller()private gridScroller: Scroller = new Scroller()private touchDown: boolean = falseprivate listTouchDown: boolean = falseprivate scrolling: boolean = falseaboutToAppear() {for (let i = 0; i < 100; i++) {this.numbers.push(i)}}build() {Stack() {Column() {Row() {Text('Head')}Column() {List({ scroller: this.scroller }) {ListItem() {Grid() {GridItem() {Text('GoodsTypeList1')}.backgroundColor(this.colors[0]).columnStart(0).columnEnd(1)GridItem() {Text('GoodsTypeList2')}.backgroundColor(this.colors[1]).columnStart(0).columnEnd(1)GridItem() {Text('GoodsTypeList3')}.backgroundColor(this.colors[2]).columnStart(0).columnEnd(1)GridItem() {Text('GoodsTypeList4')}.backgroundColor(this.colors[3]).columnStart(0).columnEnd(1)GridItem() {Text('GoodsTypeList5')}.backgroundColor(this.colors[4]).columnStart(0).columnEnd(1)}.scrollBar(BarState.Off).columnsGap(15).rowsGap(10).rowsTemplate('1fr 1fr 1fr 1fr 1fr').columnsTemplate('1fr').width('100%').height(200)}ListItem() {Grid(this.gridScroller) {ForEach(this.numbers, (item: number) => {GridItem() {Text(item + '').fontSize(16).backgroundColor(0xF9CF93).width('100%').height('100%').textAlign(TextAlign.Center)}.width('100%').height(40).shadow({ radius: 10, color: '#909399', offsetX: 1, offsetY: 1 }).borderRadius(10).translate({ x: 0, y: this.translateY })}, (item: string) => item)}.columnsTemplate('1fr 1fr').friction(0.3).columnsGap(15).rowsGap(10).scrollBar(BarState.Off).width('100%').height('100%').layoutDirection(GridDirection.Column).nestedScroll({scrollForward: NestedScrollMode.PARENT_FIRST,scrollBackward: NestedScrollMode.SELF_FIRST}).onTouch((event: TouchEvent) => {if (event.type == TouchType.Down) {this.listTouchDown = true} else if (event.type == TouchType.Up) {this.listTouchDown = false}})}}.scrollBar(BarState.Off).edgeEffect(EdgeEffect.None).onTouch((event: TouchEvent) => {if (event.type == TouchType.Down) {this.touchDown = true} else if (event.type == TouchType.Up) {this.touchDown = false}}).onScrollFrameBegin((offset: number, state: ScrollState) => {if (this.scrolling && offset > 0) {let newOffset = this.scroller.currentOffset().yOffsetif (newOffset >= 590) {this.gridScroller.scrollBy(0, offset)return { offsetRemain: 0 }} else if (newOffset + offset > 590) {this.gridScroller.scrollBy(0, newOffset + offset - 590)return { offsetRemain: 590 - newOffset }}}return { offsetRemain: offset }}).onScrollStart(() => {if (this.touchDown && !this.listTouchDown) {this.scrolling = true}}).onScrollStop(() => {this.scrolling = false})}.width('100%').height('100%').padding({ left: 10, right: 10 })}Row() {Text('Top').width(30).height(30).borderRadius(50)}.padding(5).borderRadius(50).backgroundColor('#ffffff').shadow({ radius: 10, color: '#909399', offsetX: 1, offsetY: 1 }).margin({ right: 22, bottom: 15 }).onClick(() => {this.scroller.scrollTo({ xOffset: 0, yOffset: 0 })this.gridScroller.scrollTo({ xOffset: 0, yOffset: 0 })})}.align(Alignment.BottomEnd)}
}
示例5
- 设置属性editMode(true)设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem。
- 在onItemDragStart回调中设置拖拽过程中显示的图片。
- 在onItemDrop中获取拖拽起始位置,和拖拽插入位置,并在onItemDrop中完成交换数组位置逻辑。
说明:
预览器窗口不支持显示拖拽跟手。
@Entry
@Component
struct GridExample {@State numbers: string[] = []scroller: Scroller = new Scroller()@State text: string = 'drag'@Builder pixelMapBuilder() { //拖拽过程样式Column() {Text(this.text).fontSize(16).backgroundColor(0xF9CF93).width(80).height(80).textAlign(TextAlign.Center)}}aboutToAppear() {for (let i = 1;i <= 15; i++) {this.numbers.push(i + '')}}changeIndex(index1: number, index2: number) { //交换数组位置let temp: string;temp = this.numbers[index1];this.numbers[index1] = this.numbers[index2];this.numbers[index2] = temp;}build() {Column({ space: 5 }) {Grid(this.scroller) {ForEach(this.numbers, (day: string) => {GridItem() {Text(day).fontSize(16).backgroundColor(0xF9CF93).width(80).height(80).textAlign(TextAlign.Center)}})}.columnsTemplate('1fr 1fr 1fr').columnsGap(10).rowsGap(10).onScrollIndex((first: number) => {console.info(first.toString())}).width('90%').backgroundColor(0xFAEEE0).height(300).editMode(true) //设置Grid是否进入编辑模式,进入编辑模式可以拖拽Grid组件内部GridItem.onItemDragStart((event: ItemDragInfo, itemIndex: number) => { //第一次拖拽此事件绑定的组件时,触发回调。this.text = this.numbers[itemIndex]return this.pixelMapBuilder() //设置拖拽过程中显示的图片。}).onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { //绑定此事件的组件可作为拖拽释放目标,当在本组件范围内停止拖拽行为时,触发回调。// isSuccess=false时,说明drop的位置在grid外部;insertIndex > length时,说明有新增元素的事件发生if (!isSuccess || insertIndex >= this.numbers.length) {return}console.info('beixiang' + itemIndex + '', insertIndex + '') //itemIndex拖拽起始位置,insertIndex拖拽插入位置this.changeIndex(itemIndex, insertIndex)})}.width('100%').margin({ top: 5 })}
}
示例图:
网格子组件开始拖拽:
网格子组件拖拽过程中:
网格子组件1与子组件6拖拽交换位置后:
示例6
layoutDirection、maxcount、minCount、cellLength的使用。
@Entry
@Component
struct GridExample {@State numbers: string[] = []aboutToAppear() {for (let i = 1; i <= 30; i++) {this.numbers.push(i + '')}}build() {Scroll() {Column({ space: 5 }) {Blank()Text('rowsTemplate、columnsTemplate都不设置layoutDirection、maxcount、minCount、cellLength才生效').fontSize(15).fontColor(0xCCCCCC).width('90%')Grid() {ForEach(this.numbers, (day: string) => {GridItem() {Text(day).fontSize(16).backgroundColor(0xF9CF93)}.width(40).height(80).borderWidth(2).borderColor(Color.Red)}, (day: string) => day)}.height(300).columnsGap(10).rowsGap(10).backgroundColor(0xFAEEE0).maxCount(6).minCount(2).cellLength(0).layoutDirection(GridDirection.Row)}.width('90%').margin({ top: 5, left: 5, right: 5 }).align(Alignment.Center)}}
}
最后,有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(Harmony NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(Harmony NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
鸿蒙(Harmony NEXT)最新学习路线
-
HarmonOS基础技能
- HarmonOS就业必备技能
- HarmonOS多媒体技术
- 鸿蒙NaPi组件进阶
- HarmonOS高级技能
- 初识HarmonOS内核
- 实战就业级设备开发
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
《鸿蒙 (OpenHarmony)开发入门教学视频》
《鸿蒙生态应用开发V2.0白皮书》
《鸿蒙 (OpenHarmony)开发基础到实战手册》
OpenHarmony北向、南向开发环境搭建
《鸿蒙开发基础》
- ArkTS语言
- 安装DevEco Studio
- 运用你的第一个ArkTS应用
- ArkUI声明式UI开发
- .……
《鸿蒙开发进阶》
- Stage模型入门
- 网络管理
- 数据管理
- 电话服务
- 分布式应用开发
- 通知与窗口管理
- 多媒体技术
- 安全技能
- 任务管理
- WebGL
- 国际化开发
- 应用测试
- DFX面向未来设计
- 鸿蒙系统移植和裁剪定制
- ……
《鸿蒙进阶实战》
- ArkTS实践
- UIAbility应用
- 网络案例
- ……
获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。
相关文章:

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Grid)
网格容器,由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 仅支持GridItem…...

Docker使用(四)Docker常见问题分析和解决收集整理
Docker使用(四)Docker常见问题分析和解决收集整理 五、常见问题 1、 启动异常 【描述】: 【分析】:[rootlocalhost ~]# systemctl status docker 【解决】: (1)卸载后重新安装,不能解决这个问题。 …...

通过spring boot/redis/aspect 防止表单重复提交【防抖】
一、啥是防抖 所谓防抖,一是防用户手抖,二是防网络抖动。在Web系统中,表单提交是一个非常常见的功能,如果不加控制,容易因为用户的误操作或网络延迟导致同一请求被发送多次,进而生成重复的数据记录。要针…...

C++ 作业 24/3/14
1、成员函数版本实现算术运算符的重载;全局函数版本实现算术运算符的重载 #include <iostream>using namespace std;class Test {friend const Test operator-(const Test &L,const Test &R); private:int c;int n; public:Test(){}Test(int c,int n…...

新品牌推广怎么做?百度百科创建是第一站
创业企业的宣传推广怎么做?对于初创的企业、或者品牌来说,推广方式都有一个循序渐进的过程,但多数领导者都会做出同一选择,第一步就是给自己的企业创建一个百度百科词条。在百度百科建立自己的企业、或产品词条,不仅可以树立相关信…...

k8s系列-kubectl 命令快速参考
这些指令适用于 Kubernetes v1.29。要检查版本,请使用 kubectl version 命令。 我们经常用到 --all-namespaces 参数,你应该要知道它的简写: kubectl -AKubectl 上下文和配置 kubectl config view # 显示合并的 kubeconfig 配置# 同时使用多…...

微信小程序--开启下拉刷新页面
1、下拉刷新获取数据enablePullDownRefresh 开启下拉刷新: enablePullDownRefreshbooleanfalse是否开启当前页面下拉刷新 案例: 下拉刷新,获取新的列表数据,其实就是进行一次新的网络请求: 第一步:在.json文件中开…...

【研发日记】Matlab/Simulink技能解锁(五)——Simulink布线技巧
前言 见《【研发日记】Matlab/Simulink技能解锁(一)——在Simulink编辑窗口Debug》 见《【研发日记】Matlab/Simulink技能解锁(二)——在Function编辑窗口Debug》 见《【研发日记】Matlab/Simulink技能解锁(三)——在Stateflow编辑窗口Debug》 见《【研发日记】Matlab/Simulink…...

FPGA高端项目:FPGA基于GS2971+GS2972架构的SDI视频收发+OSD动态字符叠加,提供1套工程源码和技术支持
目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收发送本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收HLS图像缩放HLS多路视频拼接应用本方案的SDI接收HLS多路视频融合叠加应用本方案的S…...

面向对象编程第二式:继承 (Java篇)
本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人…...

2024最新小狐狸AI 免授权源码
后台安装步骤: 1、在宝塔新建个站点,php版本使用7.2 、 7.3 或 7.4,把压缩包上传到站点根目录,运行目录设置为/public 2、导入数据库文件,数据库文件是 /db.sql 3、修改数据库连接配置,配置文件是/.env 4、…...

5.69 BCC工具之runqlen.py解读
一,工具简介 runqlen工具用于分析和报告运行队列(run queue)的长度,并以直方图的形式展示。它通过在所有CPU上以99赫兹的频率对运行队列长度进行采样来工作。 在操作系统中,运行队列是指内核用来管理待执行(runnable)进程的队列。当一个进程准备好执行,但由于某些原因…...

什么软件可以改变ip地址
什么软件可以修改ip地址,想必很多朋友都在寻找类似的软件,也想知道其中的答案,也能提高自己工作的效率。 经过小编在互联网摸爬滚打这些年,测试认证和整理后,发现一款名叫深度IP转换器的软件,这个确确实实能…...

C语言-strncmp strncat strncpy长度受限制的字符串函数
strncmp strncat strncpy长度受限制的字符串函数 首先 我们需要知道 这几个的语法格式差不多 这里传递的size_t的长度是传递的字节长度 不是个数 也就这里int*是四个字节 char*是一个字节 如果是整数进行交换 。此时也就需要20个字节,这样可以交换五个整数 这里差…...

ROS Kinetic通信编程:话题、服务、动作编程
文章目录 一、话题编程二、服务编程三、动作编程 接上篇,继续学习ROS通信编程基础 一、话题编程 步骤: 创建发布者 初始化ROS节点向ROS Master注册节点信息,包括发布的话题名和话题中的消息类型按照一定频率循环发布消息 创建订阅者 初始化…...

还原wps纯粹的编辑功能
1.关闭稻壳模板: 1.1. 启动wps(注意不要乱击稻壳模板,点了就找不到右键菜单了) 1.2. 在稻壳模板选项卡右击:选不再默认展示 2.关闭托盘中wps云盘图标:右击云盘图标/同步与设置: 2.1.关闭云文档同步 2.2.窗口选桌面应用…...

【烹饪】清炒菠菜的学习笔记
1 焯水:15s左右即可 Kimi教授 菠菜含有草酸,与含钙丰富的食物共煮时可能会形成草酸钙,影响钙的吸收,因此在烹饪时通常建议先用开水烫一下菠菜以减少草酸含量。 2 可选调料:鸡精...

AcWing 4964.子矩阵
首先就是运用了暴力的思路,能够过个70%的数据,剩下的直接时间超时了,没办法优化了。 讲一下暴力的思路: 其实就是模拟而已,也就是看作想要找的矩阵为一个小窗口,然后不断移动的事而已。 #include<ios…...

代码随想录算法训练营第day20|530.二叉搜索树的最小绝对差 、 501.二叉搜索树中的众数 、236. 二叉树的最近公共祖先
530.二叉搜索树的最小绝对差 力扣题目链接 (opens new window) 给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。 示例: 提示:树中至少有 2 个节点。 二叉搜索树是一颗有序的树,可以通过中…...

Hystrix的原理及应用:构建微服务容错体系的利器(二)
本系列文章简介: 本系列文章旨在深入剖析Hystrix的原理及应用,帮助大家理解其如何在微服务容错体系中发挥关键作用。我们将从Hystrix的核心原理出发,探讨其隔离、熔断、降级等机制的实现原理;接着,我们将结合实际应用场…...

【nuget】如何移动 nuget 缓存文件夹
如何移动 nuget 缓存文件夹 一、了解NuGet包的默认存放路径二、为什么需要修改NuGet包的默认存放路径?使用下面的命令查看本地包位置三、更改下载的NuGet包存储位置四、修改VS离线包引用地址五、验证修改的新路径是否成功默认情况下,NuGet下载的包存放在系统盘(C盘中),这样一…...

H266开源视频编码器VVENC现状
VVenC 是由 Fraunhofer HHI 研究团队开发的,主要是视频编码系统组。HHI 是欧洲最大的研究组织 Fraunhofer 协会的成员,该协会是德国的一个大型非营利性组织。源代码在: https://github.com/fraunhoferhhi/vvenc VVenC几乎与H.266视频标准同时…...

unity webgl怎么获取当前页面网址
在Unity WebGL中,你可以使用Javascript和C#的互操作性来获取当前页面的网址。以下是如何实现的步骤和示例代码: 首先,你需要创建一个Javascript脚本来获取当前页面的网址。(简单方法为:创建xxx.txt,修改文件…...

深度学习神经网络训练环境配置以及演示
🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:高性能(HPC)开发基础教程 🎀CSDN主页 发狂的小花 🌄人生秘诀:学习的本质就是极致重复! 目录 1 NVIDIA Dr…...

[嵌入式AI从0开始到入土]16_ffmpeg_ascend编译安装及性能测试
[嵌入式AI从0开始到入土]嵌入式AI系列教程 注:等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间,后期会考虑出视频教程,务必催更,以防我变身鸽王。 第1期 昇腾Altas 200 DK上手 第2期 下载昇腾案例并运行 第3期 官…...

HTML5:七天学会基础动画网页11
CSS3动画 CSS3过渡的基本用法: CSS3过渡是元素从一种样式逐渐改变为另一种样式的效果。 过渡属性-transition 值与说明 transition-property 必需,指定CSS属性的name,transition效果即哪个属性发生过渡。 transition-duration 必需,t…...

考虑开发容器的 6 个理由
虽然在容器环境内进行开发的行为可以追溯到 2010 年代中期,但开发容器本身在过去一年中已经开始流行。微软在 2022 年推出了开发容器规范,推动了这一概念的发展,而 Docker 在去年夏天也紧随其后,推出了开发环境功能的测试版。 开…...

Python基础入门 --- 1-2.字面量
文章目录 Python基础入门第一章:1.1 第一个python程序 第二章 :2.1 字面量2.2 常用的值类型2.3 字符串2.3.1 三种定义方式2.3.2 引号嵌套2.3.3 字符串拼接2.3.4 字符串格式化2.3.5 格式化的精度控制数字精度控制: 2.3.6 字符串格式化方式22.3…...

华为云计算hcie认证考什么?华为hciie认证好考吗
1.理论知识:HCIE认证首先要求考生具备扎实的云计算理论基础,包括云计算的基本概念、架构、关键技术、安全管理等方面的知识。考生需要深入理解云计算的核心原理,以及华为云计算产品的特点和优势。 2.实践技能:除了理论知识外&…...

redis spring cache
数据库的数据是存储在硬盘上的,频繁访问性能较低。如果将一些需要频繁查询的热数据放到内存的缓存中,可以大大减轻数据库的访问压力。 SpringCache SpringCache提供基本的Cache抽象,并没有具体的缓存能力,需要配合具体的缓存实现…...