一、前言

最近一向在写HramonyOS,好久没有输出心情了。今日憋不住了,特定来输出一下。今日来说下List。List在APP里面是必不可少的,在Android中改写还十分简略的,无论是运用DiffUtil,还是直接notifiDataChange()。

在HarmonyOS中,运用IDataSource进行懒加载,@ObjectLink、@Observed、@State组合进行改写是必不可少的了。让我们来捋一捋。

假如您有任何疑问、对文章写的不满意、发现错误或者有更好的办法,欢迎在谈论、私信或邮件中提出,十分感谢您的支撑。

二、喝前预备

1、User

export class User {
  name: string = "帕鲁"
  age: number = 18
  occ: string = "无产阶级"
}

2、UserPage

@Entry
@Preview
@Component
struct UserPage {
  build() {}
}

3、UserItemComponent

@Component
struct UserItemComponent {
  build() {}
}

4、 CommonDataSource

太多啦~,放在文末啦。

三、榜首口ForEach

假如你的List可以直挺挺的显现在页面,不需求考虑性能、许多Item的状况下,运用ForEach即可

1、代码如下

struct UserPage {
  @State users: User[] = []
​
  aboutToAppear(): void {
    //假装有一个恳求!
    setTimeout(() => {
      //数据来了!
      this.users = [new User("仔细帕鲁"), new User("偷闲帕鲁"), new User("磨洋工帕鲁"), new User("偷闲成瘾帕鲁"), new User("超级黑奴帕鲁")]
     }, 1000)
   }
​
  build() {
    if (this.users.length > 0) {
      Column() {
        List({ space: 10 }) {
          ForEach(this.users, (item: User, index: number) => {
            ListItem() {
              Text(`${index + 1}${item.age}岁的${item.name}`)
                 .fontColor(Color.Black)
                 .fontSize(15)
                 .width('100%')
                 .textAlign(TextAlign.Center)
                 .height(50)
             }.onClick(()=>{
              this.users.push(new User("聪明帕鲁"))
             })
           }, (item: User, index: number) => item.name)
         }
         .width('100%')
         .height('100%')
       }
       .width('100%')
       .height('100%')
     }
   }
}

运用@State来监听users的长度改变,底层会主动改写列表。

onClick中,添加了一个“聪明帕鲁”。当你多次触发点击事件,你会发现列表并不会改写。因为运用了item.name作为key。底层判定为相同的Item。考虑运用 item.name+index

四、第二口LazyForEach

列表不考虑懒加载,一次性加载完的状况毕竟是少量,所以推荐运用LazyForEach。

1、直接将ForEach替换为LazyForEach

HarmonyOS一杯冰美式的时刻 -- 列表

可以看到,他期望传入的目标是一个实现了IDataSource的目标。

2、CommonDataSource

在官方示例中随意找个实现了IDataSource的类即可

代码太多,贴个图 (在文章结尾找代码吧~)

HarmonyOS一杯冰美式的时刻 -- 列表

3、修正后

struct UserPage {
  @State users: CommonDataSource<User> = new CommonDataSource()
​
  aboutToAppear(): void {
    //假装有一个恳求!
    setTimeout(() => {
      //数据来了!
      this.users.setData([new User("仔细帕鲁"), new User("偷闲帕鲁"), new User("磨洋工帕鲁"), new User("偷闲成瘾帕鲁"), new User("超级黑奴帕鲁")])
     }, 1000)
   }
​
  build() {
    if (this.users.isNotEmpty()) {
      Column() {
        List({ space: 10 }) {
          LazyForEach(this.users, (item: User, index: number) => {
            ListItem() {
              Text(`${index + 1}只${item.age}岁的${item.name}`)
                 .fontColor(Color.Black)
                 .fontSize(15)
                 .width('100%')
                 .textAlign(TextAlign.Center)
                 .height(50)
             }
           }, (item: User, index: number) => item.name)
         }
         .cachedCount(10)
         .width('100%')
         .height('100%')
       }
       .width('100%')
       .height('100%')
     }
​
   }
}

基本没有什么区别。

4、改写

在杂乱的列表中,最好生成一个十分共同的Key,比方JSON.stringify(item),此时修正、添加、删去、位移等操作,需求调用对应的notifyDataChange办法。比方下面修正了点击的年龄:

LazyForEach(this.users, (item: User, index: number) => {
   ListItem() {
     Text(`${index + 1}只${item.age}岁的${item.name}`)
       .fontColor(Color.Black)
       .fontSize(15)
       .width('100%')
       .textAlign(TextAlign.Center)
       .height(50)
   }.onClick(()=>{
     item.age = index + 1
     this.users.notifyDataChange(index)
   })
}, (item: User, index: number) => JSON.stringify(item))

五、第三口@ObjectLink

修正数据之后,运用notifyDataChange来改写,让我们回到了Android 的 notifyDataChange 开发,稍感不适。有没有像DiffUtil一样,体系自己去找不同呢?

@ObjectLink是个好主意,运用起来也很简略;

1、User运用@Observed注解

@Observed
export class User {
  name: string = "帕鲁"
  age: number = 18constructor(name: string) {
    this.name = name
    this.age = Math.floor(Math.random() * 101)
   }
}

2、将ListItem内容运用单独的Commpont声明

留意,将每个Item对应的数据,运用@ObjectLink声明

@Component
export struct UserItemComponent {
  @State index: number = 0
  @ObjectLink data: User
  build() {
    Text(`${this.index + 1}只${this.data.age}岁的${this.data.name}`)
       .fontColor(Color.Black)
       .fontSize(15)
       .width('100%')
       .textAlign(TextAlign.Center)
       .height(50)
   }
}

3、修正后

只粘贴了build代码,因为没有任何改变,主要还是在于User目标的修正和UserItemComponent的抽出。

可以看到onClick,直接修正目标即可。

build() {
   if (this.users.isNotEmpty()) {
     Column() {
       List({ space: 10 }) {
         LazyForEach(this.users, (item: User, index: number) => {
           ListItem() {
             UserItemComponent({ data: item, index: index })
           }.onClick(() => {
             item.age = index + 1
           })
         }, (item: User, index: number) => JSON.stringify(item))
       }
       .cachedCount(10)
       .width('100%')
       .height('100%')
     }
     .width('100%')
     .height('100%')
   }
}

至此,一个简略的列表就好了。

六、喝完:为什么我的ObjectLink不收效

相信有同学遇到过,运用@ObjectLink处理网络恳求回来的JSON数据时,修正列表数据后列表不改写的状况。

其实问题很简略:通过JSON.parse得到的目标并不是通过User构造出的实例,其数据改变无法被观测到,所以不能实现ui改写

解决办法,说简略,也很杂乱….

1、直接遍历所有目标。运用Object相关 + new 从头创建整个目标。

想必你也觉得这种办法很蠢

2、运用三方库 reflect-metadata 和 class-transformer

你需求在接受List目标的地方运用@Type来声明…..

别忘了“import ‘reflect-metadata’;”,不然编译会报错。

import { Type } from 'class-transformer'
import 'reflect-metadata';
export class UserList {
  @Type (() => User)
  data ?: User[]
}

在运用的地方:

let reslut : UserList = plainToClass(UserList, jsonString);

蛋疼的是API 11 并不能把plainToClass封装到恳求层去,你只能在实际调用的地方运用….蠢炸了

当然你也可以运用一个fill_class的库。假如你有更好的办法,必须一定要告诉我。

目前我知道的就两种办法了。

留意:鸿蒙的注解都只能观察本身构造出来的实例,许多不收效都可能是因为你运用的目标来历不明!

七、总结

实际上我没有TS的学习经验,所以许多思维一向在用Kotlin和Java的思维,踩了许多坑。阿弥陀佛。期望对各位有协助。

最近在预备考试和适配API11,所以头很大。不过假如是评论HarmonyOS 之类的问题请留言吧。或者你需求什么功能,虽然告诉我。我应该把一个彻底不明白TS的Android到熟练写HarmonyOS路上的坑,踩了个遍

最终、假如您有任何疑问、对文章写的不满意、发现错误或者有更好的办法,欢迎在谈论、私信或邮件中提出,十分感谢您的支撑。