Как предотвратить ненужную перекомпоновку в Jetpack Compose
У меня есть поисковый запрос и
Button
который выполнит поиск, и результаты будут показаны в столбце. Поскольку поиск занимает несколько секунд, я хочу, чтобы он выполнялся при нажатии кнопки, а не при изменении текста.
Вот упрощенная демонстрация:
Column {
val list = remember { mutableStateListOf<String>() }
val textFieldValue = remember { mutableStateOf(TextFieldValue("")) }
TextField(
value = textFieldValue.value,
onValueChange = { textFieldValue.value = it }
)
Button({
list.clear()
list.addAll(textFieldValue.value.text.split(""))
}) {
Text("Search")
}
list.forEach {
println("test")
Text(it)
}
}
После первого нажатия кнопки цикл foreach запускается при изменении текста. Даже нажав на
TextField
перезапустит цикл. Это не запускает поиск при изменении текста, но повторно отображает результаты, что вызывает сбои при вводе текста в текстовое поле.
Как этого избежать?
3 ответа
Изменение значения всегда приводит к перекомпоновке всех представлений, которые являются его прочитанным значением.
Any changes to value will schedule recomposition of any composable functions that read value.
документация
Способ остановить это - переместить все представления, считывающие значение, в отдельное представление. Он будет перекомпонован для каждого
remember
значение изменится, но это не повлияет на контейнер.
В вашем случае это довольно просто: просто переместите
TextField
и пройти
textFieldValue
в новую функцию. Вы можете переслать все необходимые параметры, например
modifier
,
textStyle
, так далее.
@Composable
fun TestView(
) {
Column {
val textFieldValue = remember { mutableStateOf(TextFieldValue("")) }
val list = remember { mutableStateListOf<String>("test") }
TextField(textFieldValue)
Button({
list.clear()
list.addAll(textFieldValue.value.text.split(""))
}) {
Text("Search")
}
list.forEach {
println("test $it")
Text(it)
}
}
}
@Composable
fun TextField(
textFieldValue: MutableState<TextFieldValue>,
) {
TextField(
value = textFieldValue.value,
onValueChange = { textFieldValue.value = it }
)
}
Я не уверен, почему нет системной функции с этой семантикой, но в compose они предпочитают , чтобы шаблон подъема состояния соответствовал UDF.
Я не предпочитаю переезжать
MutableState<>
в качестве параметра, как будто я никогда не использую
LiveData<>
как параметр. Вместо этого вы можете превратить чтение в лямбду:
@Composable
fun TextField(value: ()->TextFieldValue,
onValueChange(TextFieldValue)->Unit) {
TextField(
value = value(),
onValueChange = { onValueChange(it) }
)
}
// call like
TextField(
value = { textFieldValue.value },
onValueChange = { textFieldValue.value = it }
)
Column {
val list = remember { mutableStateListOf<String>() }
var textFieldValue = remember { mutableStateOf(TextFieldValue("")) }
var searchTerm = remember { textFieldValue.value.text.copy() }
TextField(
value = textFieldValue.value,
onValueChange = { textFieldValue.value = it }
)
Button({
searchTerm = textFieldValue.value.text.copy()
list.clear()
list.addAll(searchTerm.text.split(""))
}) {
Text("Search")
}
list.forEach {
println("test")
Text(it)
}
}
Попробуй это