Jetpack Compose – LazyColumn 不重组

Som*_*boy 9 android kotlin kotlin-coroutines android-jetpack-compose kotlin-flow

我的 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) }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

视图模型

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

该模型


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,
)
Run Code Online (Sandbox Code Playgroud)

存储库

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
    }

}
Run Code Online (Sandbox Code Playgroud)

Flow正如您在我的 logcat 中看到的那样,幼崽正在生成更新的值

逻辑猫

我已经将打印语句放在我的组合物中,并且在流发出新值后它们不会被重新组合。

编辑。

看起来像 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
}
Run Code Online (Sandbox Code Playgroud)

Com*_*are 7

Flow正如您在我的 logcat 中看到的那样,幼崽正在生成更新的值

不完全是。

Flow是发射相同List相同的Puppy对象。我相信 Compose 看到的List是与List以前相同的对象,并假设没有任何变化。

我建议的更改:

  • MakePuppy是一个不可变的data类(即,没有var属性)

  • 摆脱changeFlowgetPuppies()归还马厩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
    }
Run Code Online (Sandbox Code Playgroud)