一、前言
最近一向在写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
可以看到,他期望传入的目标是一个实现了IDataSource的目标。
2、CommonDataSource
在官方示例中随意找个实现了IDataSource的类即可
代码太多,贴个图 (在文章结尾找代码吧~)
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 = 18
constructor(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路上的坑,踩了个遍
最终、假如您有任何疑问、对文章写的不满意、发现错误或者有更好的办法,欢迎在谈论、私信或邮件中提出,十分感谢您的支撑。