鸿蒙开发之拖拽表格(API12)

336 阅读3分钟

1. 拖拽效果展示

2. 表格静态页面搭建

  • 定义表头以及列表数据, 设置初始数据源
    • head -- 表头
    • data -- 数据源
  @State table: ListModel = new ListModel({
    head: [{
      title: '姓名',
      name: 'name',
      index: 0
    }, {
      title: '年龄',
      name: 'age',
      index: 1
    }, {
      title: '性别',
      name: 'sex',
      index: 2
    }],
    data: [
      { name: '张三', age: 20, sex: '男' },
      { name: '李四', age: 19, sex: '女' },
      { name: '王五', age: 20, sex: '男' },
      { name: '赵六', age: 17, sex: '女' },
      { name: '老高', age: 28, sex: '男' }]
  })
  • 新建viewModel文件夹 新建 List 文件定义数据类型
export interface List {
  head: headItem[]
  data: PeopleItem[]
}


export interface PeopleItem {
  name: string
  age: number
  sex: "男" | "女"
}


export interface headItem {
  title: string
  name: string
  index: number
}

export class ListModel implements List {
  head: headItem[] = []
  data: PeopleItem[] = []

  constructor(model: List) {
    this.head = model.head
    this.data = model.data
  }
}

export class PeopleItemModel implements PeopleItem {
  name: string = ''
  age: number = 0
  sex: "男" | "女" = "男"

  constructor(model: PeopleItem) {
    this.name = model.name
    this.age = model.age
    this.sex = model.sex
  }
}

export class headItemModel implements headItem {
  title: string = ''
  name: string = ''
  index: number = 0

  constructor(model: headItem) {
    this.title = model.title
    this.name = model.name
    this.index = model.index
  }
}
  • 写出相应的ui布局,布局为表格类型list组件里循环表头列表,同时设置 .listDirection(Axis.Horizontal) 属性。为横向排列方式
  build() {
    Row() {
      List() {
        ForEach(this.table.head, (v: headItem, i: number) => {
          ListItem() {
            Column({ space: 20 }) {
              Text(v.title)
                .fontSize(25)
              ForEach(this.table.data, (v2: PeopleItem, i2: number) => {
                Row() {
                  Text((v2 as object)[v.name].toString())
                }
              })
            }
            .width('100%')
            .backgroundColor(i % 2 === 1 ? '#fff67b7b' : '#ffffec69')
          }
          .backgroundColor('#ff87ecf5')
          .width('33%')
        })
      }
      .listDirection(Axis.Horizontal) // 排列方向
      .width('100%')
      .margin({
        top: 100
      })
    }
    .height('50%')
    .width('100%')
  }

3. 表格拖拽效果

getHeaderItemByIndex(index: number) {
  return this.table.head.find(item => item.index === index)
}

@Builder
getDragItemList(index: number) {
  Column() {
    Text(this.getHeaderItemByIndex(index)?.title || "")
      .backgroundColor(Color.Blue)
      .fontColor(Color.White)
      .height(50)
      .width(120)
      .textAlign(TextAlign.Center)

    List() {
      ForEach(this.table.data, (item: PeopleItem) => {
        ListItem() {
          Text((item as object)[this.getHeaderItemByIndex(index)?.name || ""].toString())
            .fontColor(Color.White)
            .width("100%")
            .textAlign(TextAlign.Center)
        }
        .height(50)
      })
    }
    .width(120)
  }
  .backgroundColor(Color.Gray)
}

/**
* event 元素坐标
* itemIndex 拖动的索引值
*/
List(){
   .... 
}
.onItemDragStart((event: ItemDragInfo, itemIndex: number) => {
  return this.getDragItemList(itemIndex)  // 返回
})
  • 当我们触发了移动列表元素,此时还需要设置 onItemDrop 属性,作用:当拖动的列表项松手释放后会触发回调
  • 跨List拖拽时,当拖拽释放的位置绑定了onItemDrop时会返回true,否则为false。List内部拖拽时,isSuccess为onItemMove事件的返回值。所以我们拖动的列表需要为list内部才可以
// 根据索引值改变表头数组顺序
changeListItemIndex(startIndex: number, toIndex: number) {
  // 替换索引以及数据
  let startTarget = new headItemModel(this.table.head[startIndex]);
  startTarget.index = toIndex
  let toTarget = new headItemModel(this.table.head[toIndex])
  toTarget.index = startIndex

  this.table.head[startIndex] = startTarget; // 替换索引
  this.table.head[toIndex] = toTarget; // 替换索引
  this.table.head.sort((a, b) => a.index - b.index) // 根据索引排序
  this.table.head = [...this.table.head]
}

List(){
   .... 
}
.onItemDragStart((event: ItemDragInfo, itemIndex: number) => {
  return this.getDragItemList(itemIndex)  // 返回
})
.onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => {
  // 判断isSuccess 和 索引是否正确,不然可能导致error闪退
  if (!isSuccess || insertIndex >= this.table.head.length) {
    return;
  }
  return this.changeListItemIndex(itemIndex, insertIndex) // 替换表头
})
  • 此时已完成全部操作

4. 源代码

  • src/main/ets/pages/Index.ets
import { headItem, headItemModel, ListModel, PeopleItem } from '../models/List'

@Entry
@Component
struct Index {
  @State table: ListModel = new ListModel({
    head: [{
      title: '姓名',
      name: 'name',
      index: 0
    }, {
      title: '年龄',
      name: 'age',
      index: 1
    }, {
      title: '性别',
      name: 'sex',
      index: 2
    }],
    data: [
      { name: '张三', age: 20, sex: '男' },
      { name: '李四', age: 19, sex: '女' },
      { name: '王五', age: 20, sex: '男' },
      { name: '赵六', age: 17, sex: '女' },
      { name: '老高', age: 28, sex: '男' }]
  })

  getHeaderItemByIndex(index: number) {
    return this.table.head.find(item => item.index === index)
  }

  @Builder
  getDragItemList(index: number) {
    Column() {
      Text(this.getHeaderItemByIndex(index)?.title || "")
        .backgroundColor(Color.Blue)
        .fontColor(Color.White)
        .height(50)
        .width(120)
        .textAlign(TextAlign.Center)

      List() {
        ForEach(this.table.data, (item: PeopleItem) => {
          ListItem() {
            Text((item as object)[this.getHeaderItemByIndex(index)?.name || ""].toString())
              .fontColor(Color.White)
              .width("100%")
              .textAlign(TextAlign.Center)
          }
          .height(50)
        })
      }
      .width(120)
    }
    .backgroundColor(Color.Gray)
  }

  changeListItemIndex(startIndex: number, toIndex: number) {
    // 替换索引以及数据
    let startTarget = new headItemModel(this.table.head[startIndex]);
    startTarget.index = toIndex
    let toTarget = new headItemModel(this.table.head[toIndex])
    toTarget.index = startIndex

    this.table.head[startIndex] = startTarget; // 替换索引
    this.table.head[toIndex] = toTarget; // 替换索引
    this.table.head.sort((a, b) => a.index - b.index) // 根据索引排序
    this.table.head = [...this.table.head]
  }

  build() {
    Row() {
      List() {
        ForEach(this.table.head, (v: headItem, i: number) => {
          ListItem() {
            Column({ space: 20 }) {
              Text(v.title)
                .fontSize(25)
              ForEach(this.table.data, (v2: PeopleItem, i2: number) => {
                Row() {
                  Text((v2 as object)[v.name].toString())
                }
              })
            }
            .width('100%')
            .backgroundColor(i % 2 === 1 ? '#fff67b7b' : '#ffffec69')
          }
          .backgroundColor('#ff87ecf5')
          .width('33%')
        })
      }
      .onItemDragStart((event: ItemDragInfo, itemIndex: number) => {
        return this.getDragItemList(itemIndex)
      })
      .onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => {
        if (!isSuccess || insertIndex >= this.table.head.length) {
          return;
        }
        return this.changeListItemIndex(itemIndex, insertIndex)
      })
      .listDirection(Axis.Horizontal) // 排列方向
      .width('100%')
      .margin({
        top: 100
      })
    }
    .height('50%')
    .width('100%')
  }
}
  • src/main/ets/viewModels/List.ets
export interface List {
  head: headItem[]
  data: PeopleItem[]
}


export interface PeopleItem {
  name: string
  age: number
  sex: "男" | "女"
}


export interface headItem {
  title: string
  name: string
  index: number
}

export class ListModel implements List {
  head: headItem[] = []
  data: PeopleItem[] = []

  constructor(model: List) {
    this.head = model.head
    this.data = model.data
  }
}

export class PeopleItemModel implements PeopleItem {
  name: string = ''
  age: number = 0
  sex: "男" | "女" = "男"

  constructor(model: PeopleItem) {
    this.name = model.name
    this.age = model.age
    this.sex = model.sex
  }
}

export class headItemModel implements headItem {
  title: string = ''
  name: string = ''
  index: number = 0

  constructor(model: headItem) {
    this.title = model.title
    this.name = model.name
    this.index = model.index
  }
}