开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情
大家都知道Compose最主要的地方就是声明式的UI,使用状态管理UI。这个最主要的实现就是重组函数了,Compose里面自己实现何时调用重组函数,调用那个重组函数,但是在使用的时候如果我们使用不当,也会造成无用的重组。下面举几个例子:
- Screen所有的组件写在一个compose函数里面
如果把所有的组件都写在一个组件里面,那边当其中一个组件需要重组的时候,那么就需要把当前重组函数所有的非重组的地方都会走一遍,这对于重组其实是一种浪费。例如如下的代码:
Column {
var count by remember {
mutableStateOf(0)
}
var expanded by remember {
mutableStateOf(false)
}
Text(text = "计数器:$count")
Button(onClick = { count++ }) {
Text(text = "累加计数器")
}
Box(modifier = Modifier.fillMaxSize()){
Log.i("test","drop down widget")
Text(text = "drop down", modifier = Modifier.clickable { expanded = !expanded })
DropdownMenu(expanded = expanded, onDismissRequest = { /*TODO*/ }) {
DropdownMenuItem(onClick = { /*TODO*/ }) {
Text(text = "Item 1")
}
DropdownMenuItem(onClick = { /*TODO*/ }) {
Text(text = "Item 2")
}
}
}
}
在这段代码里面第一是点击计数的button,会累计count的值,同时text的文本也会发生变化。另外一个功能就是box里面点击text会展示dropdown list。这个其实是两个功能,但是这当点击累计加数器按钮的时候,其实box里面的“drop down widget”也会走,那么其实这个时候就重组了没有变化的部分,同理如何点击显示dropdown按钮的时候也会再把上面没有变化的部分走一遍。这个其实是一个浪费。对于这种情况就需要把这两个composable函数,这样两部分的重组互相不影响,减少不必要的重组。
- List重组问题
先看看官方的说法:
- 当item添加到list尾部时:
那么只会重组最后添加的一个,之前添加的元素不会变。
- 添加到头部时:
之前所有的元素都进行了重组。 我们使用下面的实例代码进行一下验证:
var listdata = remember {
mutableStateListOf<String>().apply {
add("item 1")
add("item 2")
add("item 3")
}
}
Column {
listdata.forEach { itemData ->
ListItem(text = itemData)
}
LaunchedEffect(key1 = Unit ){
var count = 3
while (true){
delay(5000)
Log.i("test","add to end")
listdata.add("item $count")
delay(5000)
count++
Log.i("test","add to head")
listdata.add(0,"item $count")
}
}
}
@Composable
fun ListItem(text:String){
Log.i("test","start display text:$text")
Text(text = text)
}
这个代码初始有3个item,在LaunchedEffect里面等待5s之后在队尾插入一个元素,再过5s在对头添加一个元素,我看看log打印的结果:
2022-12-05 21:22:14.173 I/test: start display text:item 1
2022-12-05 21:22:14.177 I/test: start display text:item 2
2022-12-05 21:22:14.177 I/test: start display text:item 3
2022-12-05 21:22:19.233 I/test: add to end
2022-12-05 21:22:19.252 I/test: start display text:item 3
2022-12-05 21:22:24.235 I/test: add to head
2022-12-05 21:22:24.252 I/test: start display text:item 4
2022-12-05 21:22:24.255 I/test: start display text:item 1
2022-12-05 21:22:24.256 I/test: start display text:item 2
2022-12-05 21:22:24.258 I/test: start display text:item 3
从这个log可以看出,当添加到队尾的时候只有最后添加的元素才会重新打印,而当添加到对头的时候,所有的元素都打印了一遍,所有当插入队头或者中间的任何位置都会引起不必要的重组,那这个需要怎么处理呢,官网给的方法是添加一个key。key
的值不必是全局唯一的,只需要在调用点处调用可组合项的作用域内确保其唯一性即可。下面我们在上面的例子之上添加一个key:
listdata.forEach { itemData ->
key(itemData.id){
ListItem(text = itemData.text)
}
}
数据结构做的调整,这里就不再贴代码了,现在看看打印结果:
2022-12-05 21:35:28.989 I/test: start display text:item 1
2022-12-05 21:35:28.993 I/test: start display text:item 2
2022-12-05 21:35:28.994 I/test: start display text:item 3
2022-12-05 21:35:34.073 I/test: add to end
2022-12-05 21:35:34.084 I/test: start display text:item 4
2022-12-05 21:35:39.076 I/test: add to head
2022-12-05 21:35:39.083 I/test: start display text:item 5
现在可以看出,当插入头的时候不会再去多次的不必要的重组了。今天就分享到这里, 今天还遇到一个奇怪的问题,当我给mutablestate里面添加ArrayList类型的时候,然后给ArrayList里面添加元素却不会触发重组,这个不知道为什么,有人遇到过这样的问题吗,其实state里面提供了一个木tablestatelist这个可以兼并list的功能,也很好用,大家可以尝试一下。