[英]Jetpack Compose @Stable List<T> parameter recomposition
@Composable functions are recomposed @Composable 函数被重构
When passing items: List<Int>
as parameter, compose always recomposes, regardless of List
is immutable and cannot be changed.当传递items: List<Int>
作为参数时,compose 总是重新组合,不管List
是不可变的,不能改变。 ( List is interface without @Stable annotation ). ( List 是没有 @Stable 注释的接口)。 So any Composable function which accepts List<T>
as parameter always gets recomposed, no intelligent recomposition.因此,任何接受List<T>
作为参数的可组合 function 总是被重组,没有智能重组。
How to mark List<T>
as stable, so compiler knows that List is immutable and function never needs recomposition because of it?如何将List<T>
标记为稳定,以便编译器知道 List 是不可变的,并且 function 永远不需要重新组合?
Only way i found is wrapping like @Immutable data class ImmutableList<T>(val items: List<T>)
.我发现的唯一方法是像@Immutable data class ImmutableList<T>(val items: List<T>)
一样包装。 Demo (when Child1 recomposes Parent, Child2 with same List gets recomposed too) :演示(当 Child1 重组 Parent 时,具有相同 List 的 Child2 也被重组) :
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBasicsTheme {
Parent()
}
}
}
}
@Composable
fun Parent() {
Log.d("Test", "Parent Draw")
val state = remember { mutableStateOf(false) }
val items = remember { listOf(1, 2, 3) }
Column {
// click forces recomposition of Parent
Child1(value = state.value,
onClick = { state.value = !state.value })
//
Child2(items)
}
}
@Composable
fun Child1(
value: Boolean,
onClick: () -> Unit
) {
Log.d("Test", "Child1 Draw")
Text(
"Child1 ($value): Click to recompose Parent",
modifier = Modifier
.clickable { onClick() }
.padding(8.dp)
)
}
@Composable
fun Child2(items: List<Int>) {
Log.d("Test", "Child2 Draw")
Text(
"Child 2 (${items.size})",
modifier = Modifier
.padding(8.dp)
)
}
Using lambda, you can do this使用 lambda,您可以这样做
@Composable
fun Parent() {
Log.d("Test", "Parent Draw")
val state = remember { mutableStateOf(false) }
val items = remember { listOf(1, 2, 3) }
val getItems = remember(items) {
{
items
}
}
Column {
// click forces recomposition of Parent
Child1(value = state.value,
onClick = { state.value = !state.value })
//
Child2(items)
Child3(getItems)
}
}
@Composable
fun Child3(items: () -> List<Int>) {
Log.d("Test", "Child3 Draw")
Text(
"Child 3 (${items().size})",
modifier = Modifier
.padding(8.dp)
)
}
Another workaround is to pass around a SnapshotStateList
.另一种解决方法是传递SnapshotStateList
。
Specifically, if you use backing values in your ViewModel
as suggested in the Android codelabs, you have the same problem.具体来说,如果您按照 Android 代码实验室中的建议在ViewModel
中使用支持值,则会遇到同样的问题。
private val _myList = mutableStateListOf(1, 2, 3)
val myList: List<Int> = _myList
Composables that use myList
are recomposed even if _myList
is unchanged.即使_myList
未更改,使用myList
的可组合项也会重新组合。 Opt instead to pass the mutable list directly (of course, you should treat the list as read-only still, except now the compiler won't help you).选择直接传递可变列表(当然,您应该将列表视为只读,除非现在编译器无法帮助您)。
Example with also the wrapper immutable list:带有包装器不可变列表的示例:
@Immutable
data class ImmutableList<T>(
val items: List<T>
)
var itemsList = listOf(1, 2, 3)
var itemsImmutable = ImmutableList(itemsList)
@Composable
fun Parent() {
Log.d("Test", "Parent Draw")
val state = remember { mutableStateOf(false) }
val itemsMutableState = remember { mutableStateListOf(1, 2, 3) }
Column {
// click forces recomposition of Parent
Child1(state.value, onClick = { state.value = !state.value })
ChildList(itemsListState) // Recomposes every time
ChildImmutableList(itemsImmutableListState) // Does not recompose
ChildSnapshotStateList(itemsMutableState) // Does not recompose
}
}
@Composable
fun Child1(
value: Boolean,
onClick: () -> Unit
) {
Text(
"Child1 ($value): Click to recompose Parent",
modifier = Modifier
.clickable { onClick() }
.padding(8.dp)
)
}
@Composable
fun ChildList(items: List<Int>) {
Log.d("Test", "List Draw")
Text(
"List (${items.size})",
modifier = Modifier
.padding(8.dp)
)
}
@Composable
fun ChildImmutableList(items: ImmutableList<Int>) {
Log.d("Test", "ImmutableList Draw")
Text(
"ImmutableList (${items.items.size})",
modifier = Modifier
.padding(8.dp)
)
}
@Composable
fun ChildSnapshotStateList(items: SnapshotStateList<Int>) {
Log.d("Test", "SnapshotStateList Draw")
Text(
"SnapshotStateList (${items.size})",
modifier = Modifier
.padding(8.dp)
)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.