我的 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",
"pupper",
"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
}
答案 0 :(得分:7)
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
}
答案 1 :(得分:2)
这对我有用。
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
}
请注意,您没有必须使您的数据类不可变,但是,使其不可变将强制您必须制作元素的副本才能更新它。