文档管理中心
您当前正在浏览HarmonyOS最新文档,覆盖已发布的所有API版本,可在API参考中筛选您使用的API版本。详细的版本配套关系请参考版本说明
API参考应用框架ArkUI(方舟UI框架)ArkTS组件通用属性交互属性拖拽排序

拖拽排序

Phone12+PC/2in113+Tablet12+TV19+Wearable18+

拖拽排序用于实现列表条目或网格条目的手动排序,适用于待办列表排序、歌单管理等需要用户自定义条目顺序的场景。在List或Grid组件下使用ForEach/LazyForEach/Repeat,并设置onMove事件,每次迭代生成一个ListItem或GridItem时,可以使能拖拽排序。拖拽排序离手后,如果数据位置发生变化,将触发onMove事件,上报数据移动起始索引号和目标索引号。在onMove事件中,需要根据上报的起始索引号和目标索引号修改数据源。确保数据仅顺序发生变化,才能正常执行落位动画。

说明

onMove

Phone12+PC/2in113+Tablet12+TV19+Wearable18+

onMove(handler: Optional<OnMoveHandler>): T

拖拽排序数据移动回调。当父容器组件为ListGrid,并且ForEach/LazyForEach/Repeat每次迭代都生成一个ListItem或GridItem组件时才生效。调用后开启拖拽排序功能;拖拽排序离手后,如果数据位置发生变化,将触发handler回调,上报数据移动起始索引号和目标索引号。需要在回调中修改数据源,并确保数据仅顺序发生变化,才能正常执行落位动画。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

展开
参数名 类型 必填 说明
handler Optional<OnMoveHandler> 拖拽排序数据移动回调。当数据位置因拖拽发生变化时触发,需在回调中根据起始索引号和目标索引号修改数据源。

返回值:

展开
类型 说明
T 返回当前组件。

onMove20+

Phone20+PC/2in120+Tablet20+TV20+Wearable20+

onMove(handler: Optional<OnMoveHandler>, eventHandler: ItemDragEventHandler): T

拖拽排序数据移动回调。当父容器组件为ListGrid,并且ForEach/LazyForEach/Repeat每次迭代都生成一个ListItem或GridItem组件时才生效。调用后开启拖拽排序功能;拖拽排序离手后,如果数据位置发生变化,将触发handler回调,上报数据移动起始索引号和目标索引号。需要在回调中修改数据源,并确保数据仅顺序发生变化,才能正常执行落位动画。与onMove相比,新增eventHandler参数,可监听长按、开始拖拽、经过其他组件、拖拽结束等拖拽阶段事件。

元服务API: 从API version 20开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

展开
参数名 类型 必填 说明
handler Optional<OnMoveHandler> 拖拽排序数据移动回调。当数据位置因拖拽发生变化时触发,需在回调中根据起始索引号和目标索引号修改数据源。
eventHandler ItemDragEventHandler 拖拽事件回调集合,用于监听长按、开始拖拽、经过其他组件、拖拽结束等拖拽阶段事件。

返回值:

展开
类型 说明
T 返回当前组件。

OnMoveHandler

Phone12+PC/2in113+Tablet12+TV19+Wearable18+

type OnMoveHandler = (from: number, to: number) => void

定义数据源拖拽回调。

元服务API: 从API version 12开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

参数:

展开
参数名 类型 必填 说明
from number 数据源拖拽起始索引号。取值范围是[0, 数据源长度-1]。
to number 数据源拖拽目标索引号。取值范围是[0, 数据源长度-1]。

ItemDragEventHandler20+

Phone20+PC/2in120+Tablet20+TV20+Wearable20+

定义数据源拖拽事件回调。用于响应不同的拖拽操作。

元服务API: 从API version 20开始,该接口支持在元服务中使用。

系统能力: SystemCapability.ArkUI.ArkUI.Full

展开
名称 类型 只读 可选 说明
onLongPress Callback<number>

长按时触发的回调,不设置时不触发该回调。

- 参数index为长按时当前目标的索引号,取值范围是[0, 数据源长度-1]。

onDragStart Callback<number>

在页面跟手滑动开始时触发的回调,不设置时不触发该回调。

- 参数index为拖拽开始时当前目标的索引号,取值范围是[0, 数据源长度-1]。

onMoveThrough OnMoveHandler

在页面跟手滑动过程中经过其他组件时触发的回调,不设置时不触发该回调。

- 参数from为拖拽起始索引号,参数to为当前经过的目标索引号,取值范围均是[0, 数据源长度-1]。

onDrop Callback<number>

在页面跟手滑动结束时触发的回调,不设置时不触发该回调。

- 参数index为拖拽结束时当前目标的索引号,取值范围是[0, 数据源长度-1]。

示例

示例1(List使用OnMove进行拖拽)

以下示例展示了ForEach在List组件内使用时的拖拽效果。

收起
自动换行
深色代码主题
复制
  1. @Entry
  2. @Component
  3. struct ForEachSort {
  4. @State arr: Array<string> = [];
  5. build() {
  6. Row() {
  7. List() {
  8. ForEach(this.arr, (item: string) => {
  9. ListItem() {
  10. Text(item)
  11. .fontSize(16)
  12. .textAlign(TextAlign.Center)
  13. .size({height: 100, width: '100%'})
  14. }.margin(10)
  15. .borderRadius(10)
  16. .backgroundColor('#FFFFFFFF')
  17. }, (item: string) => item)
  18. .onMove((from: number, to: number) => {
  19. // 根据拖拽起止索引移动数据,确保数据顺序与拖拽结果一致。
  20. let tmp = this.arr.splice(from, 1);
  21. this.arr.splice(to, 0, tmp[0]);
  22. })
  23. }
  24. .width('100%')
  25. .height('100%')
  26. .backgroundColor('#FFDCDCDC')
  27. }
  28. }
  29. aboutToAppear(): void {
  30. for (let i = 0; i < 100; i++) {
  31. this.arr.push(i.toString());
  32. }
  33. }
  34. }

示例2(List使用OnMove进行拖拽,并设置拖拽事件回调)

从API version 20开始,以下示例展示了ForEach在List组件设置拖拽效果后触发的回调事件。

收起
自动换行
深色代码主题
复制
  1. // xxx.ets
  2. @Entry
  3. @Component
  4. struct ListOnMoveExample {
  5. @State arr: number[] = [0, 1, 2, 3, 4, 5, 6];
  6. build() {
  7. Column() {
  8. List({ space: 20, initialIndex: 0 }) {
  9. ForEach(this.arr, (item: number) => {
  10. ListItem() {
  11. Text('第一个List' + item)
  12. .width('100%')
  13. .height(80)
  14. .fontSize(16)
  15. .textAlign(TextAlign.Center)
  16. .borderRadius(10)
  17. .backgroundColor(0xFFFFFF)
  18. }
  19. }, (item: number) => item.toString())
  20. .onMove((from: number, to: number) => {
  21. // 根据拖拽起止索引移动数据,确保数据顺序与拖拽结果一致。
  22. let tmp = this.arr.splice(from, 1);
  23. this.arr.splice(to, 0, tmp[0]);
  24. console.info('List onMove From: ' + from);
  25. console.info('List onMove To: ' + to);
  26. },
  27. {
  28. onLongPress: (index: number) => {
  29. console.info('List onLongPress: ' + index);
  30. },
  31. onDrop: (index: number) => {
  32. console.info('List onDrop: ' + index);
  33. },
  34. onDragStart: (index: number) => {
  35. console.info('List onDragStart: ' + index);
  36. },
  37. onMoveThrough: (from: number, to: number) => {
  38. console.info('List onMoveThrough From: ' + from);
  39. console.info('List onMoveThrough To: ' + to);
  40. }
  41. }
  42. )
  43. }.width('90%')
  44. .scrollBar(BarState.Off)
  45. }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })
  46. }
  47. }

示例3(Grid规则布局使用ForEach的onMove进行拖拽,并设置拖拽事件回调)

从API版本26.0.0开始,以下示例展示了ForEach在Grid组件设置拖拽效果后触发的回调事件,Grid里全是规则的GridItem。

收起
自动换行
深色代码主题
复制
  1. // xxx.ets
  2. @Entry
  3. @Component
  4. struct GridOnMoveExample {
  5. private arr: Array<string> = [];
  6. build() {
  7. Row() {
  8. Grid() {
  9. ForEach(this.arr, (item: string) => {
  10. GridItem() {
  11. Text(item.toString())
  12. .fontSize(16)
  13. .textAlign(TextAlign.Center)
  14. .size({height: 100, width: '100%'})
  15. }.margin(10)
  16. .borderRadius(10)
  17. .backgroundColor(0xF9CF93)
  18. }, (item: string) => item)
  19. // 当拖拽松手时,被拖拽项落位位置与拖拽前不同时触发,from为起始索引,to为目标索引
  20. .onMove((from: number, to: number) => {
  21. let tmp = this.arr.splice(from, 1); // 从原位置取出被拖拽元素
  22. this.arr.splice(to, 0, tmp[0]); // 将取出的被拖拽元素插入到目标位置
  23. console.info('Grid onMove From: ' + from);
  24. console.info('Grid onMove To: ' + to);
  25. },
  26. {
  27. onLongPress: (index: number) => {
  28. // GridItem长按浮起时触发
  29. console.info('Grid onLongPress: ' + index);
  30. },
  31. onDrop: (index: number) => {
  32. // 拖拽的GridItem松手时触发
  33. console.info('Grid onDrop: ' + index);
  34. },
  35. onDragStart: (index: number) => {
  36. // GridItem长按浮起并开始拖拽时触发
  37. console.info('Grid onDragStart: ' + index);
  38. },
  39. onMoveThrough: (from: number, to: number) => {
  40. // GridItem拖拽过程中持续触发
  41. console.info('Grid onMoveThrough From: ' + from);
  42. console.info('Grid onMoveThrough To: ' + to);
  43. }
  44. }
  45. )
  46. }
  47. .columnsTemplate('1fr 1fr') // 两列等宽布局
  48. .width('100%')
  49. .height('100%')
  50. .backgroundColor(0xFAEEE0)
  51. }
  52. }
  53. aboutToAppear(): void {
  54. // 初始化100条数据作为Grid内容
  55. for (let i = 0; i < 100; i++) {
  56. this.arr.push(i.toString())
  57. }
  58. }
  59. }

示例4(Grid不规则布局使用ForEach的onMove进行拖拽,并设置拖拽事件回调)

从API版本26.0.0开始,以下示例展示了ForEach在Grid组件设置拖拽效果后触发的回调事件,Grid里存在不规则的GridItem。应用可通过irregularIndexes设置哪些索引是不规则节点,通过修改对应索引的rectSize调整该GridItem所占的行列数。

收起
自动换行
深色代码主题
复制
  1. // xxx.ets
  2. class Rects {
  3. id: number = 0
  4. // rectSize表示该GridItem占用的[行, 列]数,默认[1, 1]为规则节点
  5. rectSize: [number, number] = [1, 1]
  6. constructor(id_: number) {
  7. this.id = id_
  8. }
  9. }
  10. @Entry
  11. @Component
  12. struct GridOnMoveExample {
  13. @State arr: Array<Rects> = [];
  14. // 网格布局选项(实际生效),声明不规则节点的索引及各自占用的行列数
  15. @State layoutOptions: GridLayoutOptions = {
  16. regularSize: [1, 1],
  17. irregularIndexes: [8], // 索引为8的GridItem为不规则节点
  18. onGetIrregularSizeByIndex: (index: number) => {
  19. return this.arr[index].rectSize
  20. }
  21. };
  22. // 布局选项(备份),用于拖拽时通过整体赋值触发layoutOptions刷新
  23. layoutOptions_back: GridLayoutOptions = {
  24. regularSize: [1, 1],
  25. irregularIndexes: [8], // 索引为8的GridItem为不规则节点
  26. onGetIrregularSizeByIndex: (index: number) => {
  27. return this.arr[index].rectSize
  28. }
  29. };
  30. build() {
  31. Row() {
  32. Grid(undefined, this.layoutOptions) {
  33. ForEach(this.arr, (item: Rects) => {
  34. GridItem() {
  35. Text(item.id.toString())
  36. .fontSize(16)
  37. .textAlign(TextAlign.Center)
  38. .size({ height: 100 * item.rectSize[0] + (item.rectSize[0] - 1) * 20, width: '100%'}) // 设置高度,跨行GridItem需额外增加外边距(规则GridItem的间距为2*10)用于界面对齐
  39. }.margin(10)
  40. .borderRadius(10)
  41. .backgroundColor(0xF9CF93)
  42. }, (item: Rects) => item.id.toString())
  43. // 当拖拽松手时,被拖拽项落位位置与拖拽前不同时触发,from为起始索引,to为目标索引
  44. .onMove((from:number, to:number) => {
  45. console.info("Grid onMove from " + from + " to " + to)
  46. // 更新this.arr数据源
  47. let tmp = this.arr.splice(from, 1);
  48. this.arr.splice(to, 0, tmp[0]);
  49. if (from < to) { // 被拖拽项索引小于目标位置索引
  50. // 先保存被拖拽项在irregularIndexes数组中的位置,避免后续循环更新产生重复值后indexOf定位错误
  51. let from_idx = -1
  52. if (this.layoutOptions.irregularIndexes?.includes(from)) {
  53. from_idx = this.layoutOptions.irregularIndexes.indexOf(from)
  54. }
  55. // 被拖拽项与目标位置之间的元素整体前移一位(索引-1)
  56. if (this.layoutOptions.irregularIndexes != undefined) {
  57. let len = this.layoutOptions.irregularIndexes.length
  58. for (let i = len - 1; i >= 0; i --) {
  59. let irregularIndex = this.layoutOptions.irregularIndexes[i]
  60. if (irregularIndex > from && irregularIndex <= to) {
  61. this.layoutOptions.irregularIndexes[i] --
  62. }
  63. }
  64. }
  65. // 若被拖拽项本身为不规则节点,更新其索引到目标位置
  66. if (from_idx != -1 && this.layoutOptions.irregularIndexes != undefined) {
  67. this.layoutOptions.irregularIndexes[from_idx] = to
  68. }
  69. } else { // 被拖拽项索引大于等于目标位置索引
  70. // 先保存被拖拽项在irregularIndexes数组中的位置,避免后续循环更新产生重复值后indexOf定位错误
  71. let from_idx = -1
  72. if (this.layoutOptions.irregularIndexes?.includes(from)) {
  73. from_idx = this.layoutOptions.irregularIndexes.indexOf(from)
  74. }
  75. // 目标位置至被拖拽项之间的元素整体后移一位(索引+1)
  76. if (this.layoutOptions.irregularIndexes != undefined) {
  77. let len = this.layoutOptions.irregularIndexes.length
  78. for (let i = 0; i < len; i ++) {
  79. let irregularIndex = this.layoutOptions.irregularIndexes[i]
  80. if (irregularIndex >= to && irregularIndex < from) {
  81. this.layoutOptions.irregularIndexes[i] ++
  82. }
  83. }
  84. }
  85. // 若被拖拽项本身为不规则节点,更新其索引到目标位置
  86. if (from_idx != -1 && this.layoutOptions.irregularIndexes != undefined) {
  87. this.layoutOptions.irregularIndexes[from_idx] = to
  88. }
  89. }
  90. // 通过备份对象整体赋值,强制layoutOptions刷新生效
  91. this.layoutOptions_back.irregularIndexes = this.layoutOptions.irregularIndexes
  92. this.layoutOptions = this.layoutOptions_back
  93. console.info("Grid this.layoutOptions.irregularIndexes " + this.layoutOptions.irregularIndexes)
  94. },
  95. {
  96. onLongPress: (index: number) => {
  97. // GridItem长按浮起时触发
  98. console.info('Grid onLongPress: ' + index);
  99. },
  100. onDrop: (index: number) => {
  101. // 拖拽的GridItem松手时触发
  102. console.info('Grid onDrop: ' + index);
  103. },
  104. onDragStart: (index: number) => {
  105. // GridItem长按浮起并开始拖拽时触发
  106. console.info('Grid onDragStart: ' + index);
  107. },
  108. onMoveThrough: (from: number, to: number) => {
  109. // GridItem拖拽过程中持续触发
  110. console.info('Grid onMoveThrough From: ' + from + ' to: ' + to);
  111. }
  112. })
  113. }
  114. .columnsTemplate('1fr 1fr 1fr 1fr') // 四列等宽布局
  115. .width('100%')
  116. .height('100%')
  117. .backgroundColor(0xFAEEE0)
  118. }
  119. }
  120. aboutToAppear(): void {
  121. // 初始化100个矩形数据,并设置索引8为2x2的不规则节点
  122. for (let i = 0; i < 100; i++) {
  123. this.arr.push(new Rects(i));
  124. }
  125. this.arr[8].rectSize = [2, 2] // 2行2列
  126. }
  127. }

示例5(Grid不规则布局使用LazyForEach的onMove进行拖拽,并设置拖拽事件回调)

从API版本26.0.0开始,以下示例展示了LazyForEach在Grid组件设置拖拽效果后触发的回调事件,Grid里存在不规则的GridItem。应用可通过irregularIndexes设置哪些索引是不规则节点,通过修改对应索引的rectSize调整该GridItem所占的行列数。

收起
自动换行
深色代码主题
复制
  1. // RectGridDataSource.ets
  2. export class Rects {
  3. id: number = 0
  4. // rectSize表示该GridItem占用的[行, 列]数,默认[1, 1]为规则节点
  5. rectSize: [number, number] = [1, 1]
  6. constructor(id_: number) {
  7. this.id = id_
  8. }
  9. }
  10. // LazyForEach的数据源,实现IDataSource接口,负责管理数据及通知UI刷新
  11. export class RectGridDataSource implements IDataSource {
  12. private list: Array<Rects> = [];
  13. private listeners: DataChangeListener[] = [];
  14. constructor(list: Rects[]) {
  15. this.list = list;
  16. }
  17. // 返回数据总数
  18. totalCount(): number {
  19. return this.list.length;
  20. }
  21. // 根据索引获取对应的数据项
  22. getData(index: number): Rects {
  23. return this.list[index];
  24. }
  25. // 注册数据变更监听器
  26. registerDataChangeListener(listener: DataChangeListener): void {
  27. if (this.listeners.indexOf(listener) < 0) {
  28. this.listeners.push(listener);
  29. }
  30. }
  31. // 注销数据变更监听器
  32. unregisterDataChangeListener(listener: DataChangeListener): void {
  33. const pos = this.listeners.indexOf(listener);
  34. if (pos >= 0) {
  35. this.listeners.splice(pos, 1);
  36. }
  37. }
  38. // 通知控制器数据位置变化
  39. notifyDataMove(from: number, to: number): void {
  40. this.listeners.forEach(listener => {
  41. listener.onDataMove(from, to);
  42. })
  43. }
  44. // 重新加载所有数据
  45. notifyDataReload(): void {
  46. this.listeners.forEach(listener => {
  47. listener.onDataReloaded();
  48. })
  49. }
  50. // 将from位置的元素移动到to位置,并通知UI全部重载刷新
  51. public moveItem(from: number, to: number): void {
  52. let tmp = this.list.splice(from, 1); // 先移除被拖拽项
  53. this.list.splice(to, 0, tmp[0]); // 将被拖拽项插入到目标位置
  54. this.notifyDataReload()
  55. }
  56. }
收起
自动换行
深色代码主题
复制
  1. // xxx.ets
  2. import { RectGridDataSource, Rects } from './RectGridDataSource';
  3. @Entry
  4. @Component
  5. struct GridOnMoveExample {
  6. numbers: RectGridDataSource = new RectGridDataSource([]);
  7. // 网格布局选项(实际生效),声明不规则节点的索引及各自占用的行列数
  8. @State layoutOptions: GridLayoutOptions = {
  9. regularSize: [1, 1],
  10. irregularIndexes: [4, 5, 6, 7, 8, 13], // 设置哪些索引对应的GridItem为不规则节点
  11. onGetIrregularSizeByIndex: (index: number) => {
  12. return this.numbers.getData(index).rectSize
  13. }
  14. };
  15. // 布局选项(备份),用于拖拽时通过整体赋值触发layoutOptions刷新
  16. layoutOptions_back: GridLayoutOptions = {
  17. regularSize: [1, 1],
  18. irregularIndexes: [4, 5, 6, 7, 8, 13],
  19. onGetIrregularSizeByIndex: (index: number) => {
  20. return this.numbers.getData(index).rectSize
  21. }
  22. };
  23. build() {
  24. Row() {
  25. Grid(undefined, this.layoutOptions) {
  26. LazyForEach(this.numbers, (item: Rects) => {
  27. GridItem() {
  28. Text(item.id.toString())
  29. .fontSize(16)
  30. .textAlign(TextAlign.Center)
  31. // 设置高度,跨行GridItem需额外增加外边距(规则GridItem的间距为2*10)用于界面对齐
  32. .size({ height: 100 * item.rectSize[0] + (item.rectSize[0] - 1) * 20, width: '100%'})
  33. }.margin(10)
  34. .borderRadius(10)
  35. .backgroundColor(0xF9CF93)
  36. }, (index: Rects) => index.id.toString())
  37. // 当拖拽松手时,被拖拽项落位位置与拖拽前不同时触发,from为起始索引,to为目标索引
  38. .onMove((from:number, to:number) => {
  39. console.info("Grid onMove from " + from + " to " + to)
  40. // 更新数据源
  41. this.numbers.moveItem(from, to)
  42. if (from < to) { // 被拖拽项索引小于目标位置索引
  43. // 先保存被拖拽项在irregularIndexes数组中的位置,避免后续循环更新产生重复值后indexOf定位错误
  44. let from_idx = -1
  45. if (this.layoutOptions.irregularIndexes?.includes(from)) {
  46. from_idx = this.layoutOptions.irregularIndexes.indexOf(from)
  47. }
  48. // 被拖拽项与目标位置之间的元素整体前移一位(索引-1)
  49. if (this.layoutOptions.irregularIndexes != undefined) {
  50. let len = this.layoutOptions.irregularIndexes.length
  51. for (let i = len - 1; i >= 0; i --) {
  52. let irregularIndex = this.layoutOptions.irregularIndexes[i]
  53. if (irregularIndex > from && irregularIndex <= to) {
  54. this.layoutOptions.irregularIndexes[i] --
  55. }
  56. }
  57. }
  58. // 若被拖拽项本身为不规则节点,更新其索引到目标位置
  59. if (from_idx != -1 && this.layoutOptions.irregularIndexes != undefined) {
  60. this.layoutOptions.irregularIndexes[from_idx] = to
  61. }
  62. } else { // 被拖拽项索引大于等于目标位置索引
  63. // 先保存被拖拽项在irregularIndexes数组中的位置,避免后续循环更新产生重复值后indexOf定位错误
  64. let from_idx = -1
  65. if (this.layoutOptions.irregularIndexes?.includes(from)) {
  66. from_idx = this.layoutOptions.irregularIndexes.indexOf(from)
  67. }
  68. // 目标位置至被拖拽项之间的元素整体后移一位(索引+1)
  69. if (this.layoutOptions.irregularIndexes != undefined) {
  70. let len = this.layoutOptions.irregularIndexes.length
  71. for (let i = 0; i < len; i ++) {
  72. let irregularIndex = this.layoutOptions.irregularIndexes[i]
  73. if (irregularIndex >= to && irregularIndex < from) {
  74. this.layoutOptions.irregularIndexes[i] ++
  75. }
  76. }
  77. }
  78. // 若被拖拽项本身为不规则节点,更新其索引到目标位置
  79. if (from_idx != -1 && this.layoutOptions.irregularIndexes != undefined) {
  80. this.layoutOptions.irregularIndexes[from_idx] = to
  81. }
  82. }
  83. // 通过备份对象整体赋值,强制layoutOptions刷新生效
  84. this.layoutOptions_back.irregularIndexes = this.layoutOptions.irregularIndexes
  85. this.layoutOptions = this.layoutOptions_back
  86. console.info("Grid this.layoutOptions.irregularIndexes " + this.layoutOptions.irregularIndexes)
  87. },
  88. {
  89. onLongPress: (index: number) => {
  90. // GridItem长按浮起时触发
  91. console.info('Grid onLongPress: ' + index);
  92. },
  93. onDrop: (index: number) => {
  94. // 拖拽的GridItem松手时触发
  95. console.info('Grid onDrop: ' + index);
  96. },
  97. onDragStart: (index: number) => {
  98. // GridItem长按浮起并开始拖拽时触发
  99. console.info('Grid onDragStart: ' + index);
  100. },
  101. onMoveThrough: (from: number, to: number) => {
  102. // GridItem拖拽过程中持续触发
  103. console.info('Grid onMoveThrough From: ' + from + ' to: ' + to);
  104. }
  105. })
  106. }
  107. .columnsTemplate('1fr 1fr 1fr 1fr') // 四列等宽布局
  108. .width('100%')
  109. .height('100%')
  110. .backgroundColor(0xFAEEE0)
  111. }
  112. }
  113. aboutToAppear(): void {
  114. // 初始化100个矩形数据并设置各不规则节点的跨占尺寸
  115. let list: Rects[] = [];
  116. for (let i = 0; i < 100; i++) {
  117. list.push(new Rects(i));
  118. }
  119. list[4].rectSize = [2, 2] // 2行2列
  120. list[5].rectSize = [1, 2] // 1行2列
  121. list[6].rectSize = [1, 2] // 1行2列
  122. list[7].rectSize = [2, 1] // 2行1列
  123. list[8].rectSize = [2, 1] // 2行1列
  124. list[13].rectSize = [1, 4] // 1行4列
  125. this.numbers = new RectGridDataSource(list);
  126. }
  127. }

示例6(Grid不规则布局使用Repeat的onMove进行拖拽,并设置拖拽事件回调)

从API版本26.0.0开始,以下示例展示了Repeat在Grid组件设置拖拽效果后触发的回调事件,Grid里存在不规则的GridItem。应用可通过irregularIndexes设置哪些索引是不规则节点,通过修改对应索引的rectSize调整该GridItem所占的行列数。

收起
自动换行
深色代码主题
复制
  1. // xxx.ets
  2. class Rects {
  3. id: number = 0
  4. // rectSize表示该GridItem占用的[行, 列]数,默认[1, 1]为规则节点
  5. rectSize: [number, number] = [1, 1]
  6. constructor(id_: number) {
  7. this.id = id_
  8. }
  9. }
  10. @Entry
  11. @ComponentV2
  12. struct GridOnMoveExample {
  13. @Local arr: Array<Rects> = [];
  14. // 网格布局选项(实际生效),声明不规则节点的索引及各自占用的行列数
  15. @Local layoutOptions: GridLayoutOptions = {
  16. regularSize: [1, 1],
  17. irregularIndexes: [4, 5, 6, 7, 8, 13], // 设置哪些索引对应的GridItem为不规则节点
  18. onGetIrregularSizeByIndex: (index: number) => {
  19. return this.arr[index].rectSize
  20. }
  21. };
  22. // 布局选项(备份),用于拖拽时通过整体赋值触发layoutOptions刷新
  23. layoutOptions_back: GridLayoutOptions = {
  24. regularSize: [1, 1],
  25. irregularIndexes: [4, 5, 6, 7, 8, 13],
  26. onGetIrregularSizeByIndex: (index: number) => {
  27. return this.arr[index].rectSize
  28. }
  29. };
  30. aboutToAppear(): void {
  31. // 初始化100个矩形数据
  32. for (let i = 0; i < 100; i++) {
  33. this.arr.push(new Rects(i));
  34. }
  35. // 设置各不规则节点的跨占尺寸
  36. this.arr[4].rectSize = [2, 2] // 2行2列
  37. this.arr[5].rectSize = [1, 2] // 1行2列
  38. this.arr[6].rectSize = [1, 2] // 1行2列
  39. this.arr[7].rectSize = [2, 1] // 2行1列
  40. this.arr[8].rectSize = [2, 1] // 2行1列
  41. this.arr[13].rectSize = [1, 4] // 1行4列
  42. }
  43. build() {
  44. Column() {
  45. Grid(undefined, this.layoutOptions) {
  46. Repeat<Rects>(this.arr)
  47. // 当拖拽松手时,被拖拽项落位位置与拖拽前不同时触发,from为起始索引,to为目标索引
  48. .onMove((from: number, to: number) => {
  49. if (from == to) {
  50. return
  51. }
  52. console.info("Grid onMove from " + from + " to " + to)
  53. // 更新this.arr数据源
  54. let tmp = this.arr.splice(from, 1);
  55. this.arr.splice(to, 0, tmp[0]);
  56. if (from < to) { // 被拖拽项索引小于目标位置索引
  57. // 先保存被拖拽项在irregularIndexes数组中的位置,避免后续循环更新产生重复值后indexOf定位错误
  58. let from_idx = -1
  59. if (this.layoutOptions.irregularIndexes?.includes(from)) {
  60. from_idx = this.layoutOptions.irregularIndexes.indexOf(from)
  61. }
  62. // 被拖拽项与目标位置之间的元素整体前移一位(索引-1)
  63. if (this.layoutOptions.irregularIndexes != undefined) {
  64. let len = this.layoutOptions.irregularIndexes.length
  65. for (let i = len - 1; i >= 0; i --) {
  66. let irregularIndex = this.layoutOptions.irregularIndexes[i]
  67. if (irregularIndex > from && irregularIndex <= to) {
  68. this.layoutOptions.irregularIndexes[i] --
  69. }
  70. }
  71. }
  72. // 若被拖拽项本身为不规则节点,更新其索引到目标位置
  73. if (from_idx != -1 && this.layoutOptions.irregularIndexes != undefined) {
  74. this.layoutOptions.irregularIndexes[from_idx] = to
  75. }
  76. } else { // 被拖拽项索引大于等于目标位置索引
  77. // 先保存被拖拽项在irregularIndexes数组中的位置,避免后续循环更新产生重复值后indexOf定位错误
  78. let from_idx = -1
  79. if (this.layoutOptions.irregularIndexes?.includes(from)) {
  80. from_idx = this.layoutOptions.irregularIndexes.indexOf(from)
  81. }
  82. // 目标位置至被拖拽项之间的元素整体后移一位(索引+1)
  83. if (this.layoutOptions.irregularIndexes != undefined) {
  84. let len = this.layoutOptions.irregularIndexes.length
  85. for (let i = 0; i < len; i ++) {
  86. let irregularIndex = this.layoutOptions.irregularIndexes[i]
  87. if (irregularIndex >= to && irregularIndex < from) {
  88. this.layoutOptions.irregularIndexes[i] ++
  89. }
  90. }
  91. }
  92. // 若被拖拽项本身为不规则节点,更新其索引到目标位置
  93. if (from_idx != -1 && this.layoutOptions.irregularIndexes != undefined) {
  94. this.layoutOptions.irregularIndexes[from_idx] = to
  95. }
  96. }
  97. // 通过备份对象整体赋值,强制layoutOptions刷新生效
  98. this.layoutOptions_back.irregularIndexes = this.layoutOptions.irregularIndexes
  99. this.layoutOptions = this.layoutOptions_back
  100. console.info("Grid this.layoutOptions.irregularIndexes " + this.layoutOptions.irregularIndexes)
  101. },
  102. {
  103. onLongPress: (index: number) => {
  104. // GridItem长按浮起时触发
  105. console.info('Grid onLongPress: ' + index);
  106. },
  107. onDrop: (index: number) => {
  108. // 拖拽的GridItem松手时触发
  109. console.info('Grid onDrop: ' + index);
  110. },
  111. onDragStart: (index: number) => {
  112. // GridItem长按浮起并开始拖拽时触发
  113. console.info('Grid onDragStart: ' + index);
  114. },
  115. onMoveThrough: (from: number, to: number) => {
  116. // GridItem拖拽过程中持续触发
  117. console.info('Grid onMoveThrough From: ' + from + ' to: ' + to);
  118. }
  119. })
  120. .each((obj: RepeatItem<Rects>) => {
  121. GridItem() {
  122. Text(obj.item.id.toString())
  123. .fontSize(16)
  124. .textAlign(TextAlign.Center)
  125. // 设置高度,跨行GridItem需额外增加外边距(规则GridItem的间距为2*10)用于界面对齐
  126. .size({ height: 100 * this.arr[obj.index].rectSize[0] + (this.arr[obj.index].rectSize[0] - 1) * 20, width: '100%' })
  127. }.margin(10)
  128. .borderRadius(10)
  129. .backgroundColor(0xF9CF93)
  130. })
  131. .key((item: Rects, index: number) => {
  132. return item.id.toString();
  133. })
  134. .virtualScroll({ totalCount: this.arr.length }) // 开启虚拟滚动,仅渲染可见项以提升性能
  135. }
  136. .columnsTemplate('1fr 1fr 1fr 1fr') // 四列等宽布局
  137. .border({ width: 1 })
  138. .backgroundColor(0xFAEEE0)
  139. .width('100%')
  140. .height('100%')
  141. }
  142. }
  143. }

在 API参考 中进行搜索
请输入您想要搜索的关键词