如何解决Jetpack Compose – LazyColumn 不重组
我的 LazyColumn 没有重组,但值正在更新。
如果我向下滚动列表并向上滚动,我会看到 UI 的正确值
主活动
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyTheme {
MyApp()
}
}
}
}
// Start building your app here!
@Composable
fun MyApp(vm: PuppyListViewModel = viewModel()) {
val puppers by vm.pups.collectAsState(emptyList())
Surface(color = MaterialTheme.colors.background) {
Column {
Toolbar()
LazyColumn {
items(puppers) { pup -> PuppyUI(pup,vm::seeDetails,vm::togglePuppyAdoption) }
}
}
}
}
视图模型
class PuppyListViewModel : ViewModel() {
val pups = PuppyRepo.getPuppies().onEach {
println("FlowEmitted: $it")
}
fun togglePuppyAdoption(puppy: Puppy) = viewModelScope.launch {
PuppyRepo.toggleAdoption(puppy.id)
}
fun seeDetails(puppy: Puppy) {
println("seeDetails $puppy")
}
}
模型
internal var IDS = 0L
data class Puppy (
val name: String,val tagline: String = "",val race: String,@DrawableRes val image: Int,var adopted: Boolean = false,val id: Long = ++IDS,)
仓库
object PuppyRepo {
private val changeFlow = MutableStateFlow(0)
private val pups: List<Puppy>
private val puppyImages = listOf(
R.drawable._1,R.drawable._2,R.drawable._3,R.drawable._4,R.drawable._5,R.drawable._6,R.drawable._7,R.drawable._8,R.drawable._9,R.drawable._10,R.drawable._11,R.drawable._12,R.drawable._13,R.drawable._14,R.drawable._15,R.drawable._16,R.drawable._17,R.drawable._18,R.drawable._19,)
private val puppyNames = listOf(
"Gordie","Alice","Belle","Olivia","Bubba","Pandora","Bailey","Nala","Rosco","Butch","Matilda","Molly","Piper","Kelsey","Rufus","Duke","Ozzy"
)
private val puppyTags = listOf(
"doggo","doge","special dogo","wrinkler","corgo","shoob","puggo","pupper","small dogo","big ol dogo","woofer","floofer","yapper","good-boye","grizlord","snip-snap dogo"
)
private val puppyBreeds = listOf(
"Labrador Retriever","German Shepard","Golden Retriever","French Bulldog","Bulldog","Beagle","Poodle","Rottweiler","German Shorthaired Pointer","Yorkshire Terrier","Boxer"
)
init {
pups = puppyImages.map { image ->
val name = puppyNames.random()
val tagline = puppyTags.random()
val breed = puppyBreeds.random()
Puppy(name,tagline,breed,image)
}
}
@OptIn(ExperimentalCoroutinesApi::class)
fun getPuppies() = changeFlow.flatMapLatest { flowOf(pups) }
fun getPuppy(puppyId: Long) = flow {
emit(pups.find { it.id == puppyId })
}
suspend fun toggleAdoption(puppyId: Long): Boolean {
val found = getPuppy(puppyId).first()?.toggleAdoption()?.let { true } ?: false
if (found) {
// Trigger a new emission for those that are consuming a Flow from getPuppies
changeFlow.value = changeFlow.value + 1
}
return found
}
private fun Puppy.toggleAdoption() {
adopted = !adopted
}
}
Flow
幼崽正在生成更新的值,如您在我的 logcat 中所见
我已将打印语句放在我的可组合项上,并且在流程发出新值后它们不会被重新组合。
编辑。
Lookslike Compose 比较对象的引用,由于这些没有改变,即使流发出新值也不会发生重组(可能是 Compose 上的错误?)
更改了 toggle
功能以重新创建列表元素的实例,如下所示,现在可以正常工作了。
注意:我已经将 Puppy.adopted
变成了 val
而不是 var
suspend fun toggleAdoption(puppyId: Long): Boolean {
var found = false
pups = pups.map {
val isThePuppy = it.id == puppyId
found = found || isThePuppy
if(isThePuppy) it.copy(adopted = !it.adopted) else it.copy()
}
if (found) {
// Trigger a new emission for those that are consuming a Flow from getPuppies
changeFlow.value = changeFlow.value + 1
}
return found
}
解决方法
Flow
幼崽正在生成更新的值,如您在我的 logcat 中所见
不完全是。
Flow
发出相同 List
对象的相同 Puppy
。我相信 Compose 会发现 List
与之前的 List
对象相同,并假设没有任何变化。
我建议的更改:
-
使
Puppy
成为不可变的data
类(即没有var
属性) -
去掉
changeFlow
并让getPuppies()
返回一个稳定的MutableStateFlow<List<Puppy>>
(或者让它成为公共财产) -
在
toggleAdoption()
中,创建一个新的Puppy
对象列表并使用它来更新MutableStateFlow<List<Puppy>>
:
suspend fun toggleAdoption(puppyId: Long) {
val current = puppies.value // assumes that puppies is a MutableSharedFlow<List<Puppy>>
val replacement = current.map { if (it.id == puppyId) it.copy(adopted = !it.adopted) else it }
puppies.value = replacement
}
,
这对我有用。
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
class MyViewModel : ViewModel() {
var selectables: List<Selectable> by mutableStateOf(List(100) { Selectable(name = "$it") })
private set
fun onTapped(tappedItem: Selectable) {
val index = selectables.indexOf(tappedItem)
selectables = selectables.toMutableList().also {
it[index] = tappedItem.copy(selected = !tappedItem.selected)
}
}
}
data class Selectable(
val name: String,var selected: Boolean = false,)
关键部分是:
- 重新分配列表而不是就地修改它(例如,将
selectables
设为MutableList
并执行selectables[index] = tappedItem.copy(selected = !tappedItem.selected)
不起作用) - 重新分配所选项目而不是就地修改它,例如以下是行不通的
selectables = selectables.toMutableList().also {
it[index].selected = !tappedItem.selected
}
请注意,您没有必须使您的数据类不可变,但是,使其不可变将强制您必须制作元素的副本才能更新它。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。