Stylianos Gakis
10/15/2021, 11:23 PMRecyclerView that holds ViewHolders that just hold a ComposeView inside it, I am not getting the expected recomposition behaviour I am expecting. More details in thread 🧵Stylianos Gakis
10/15/2021, 11:23 PMRecyclerView backed by a ListAdapter with diffUtil.
My ViewHolder I am referring to looks something like this:
class MyComposeViewHolder(
private val composeView: ComposeView,
) : MainViewHolder(composeView) {
init { composeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) }
override fun bind(model: MainModel) {
composeView.setContent {
AppTheme {
Row {
AnimatedVisibility(model.someBoolean) {
// Should trigger to showing/not showing backed by `model.someBoolean`
Row {
Canvas(Modifier.size(8.dp)) { drawCircle(Color.Red) }
}
}
// Always showing composable
}
}
}
}
}
Now my problem is that I can’t manage to make it so that whenever the data changes, what actually happens is that the new model is fed inside the composable, and proper recomposition happens.
Now what happens is that a whole new view is created, and instead of getting the nice AnimatedVisibility animation I get the generic RecyclerView flickering.
The DiffUtil does indeed return true for areItemsTheSame and false for areContentsTheSame so my expected behaviour here is that the new data gets send to the bind method, not that the entire ViewHolder is initialised.
I feel like I am either not understanding something correctly, or I am doing some silly RecyclerView mistake after not having used it for so many months. Does anyone have an example of a properly implemented RecyclerView with ComposableView items inside of it? I’d love to look as some open sourced examples.Chuck Jazdzewski [G]
10/15/2021, 11:42 PMmodel.someBoolean a mutableStateOf<Boolean>() ?Albert Chang
10/16/2021, 3:59 AMbind() whenever the data changes, right? If so, you are basically changing the entire content every time discarding the old composition. You should probably do something like this:
class MyComposeViewHolder(
composeView: ComposeView,
) : MainViewHolder(composeView) {
private var model by mutableStateOf<MainModel?>(null)
init {
composeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
composeView.setContent {
// Use `model` here
}
}
override fun bind(model: MainModel) {
this.model = model
}
}Arjun Achatz
10/16/2021, 5:18 AMArjun Achatz
10/16/2021, 5:19 AMArjun Achatz
10/16/2021, 5:19 AMStylianos Gakis
10/16/2021, 9:48 AMmodel.boolean is in fact not a mutableState. And I see how initialising the composable once in the init and then changing the local nullable mutableState makes sense, these seem to be good pointers.
I feel like after the changes you’ve suggested it should work, and I am just doing something wrong on the normal RecyclerView implementation, like it runs init{} on the ViewHolder every time even if areItemsTheSame returns true. I swear I’ve just forgotten how RV works after doing so much Compose.
Does anyone have a link to a properly implemented RV that extends ListAdapter so I can do a sanity check and see what I’m messing up? If you have a sample of an RV with composables in some of its VH too that’d be perfect!
p.s:
As far as lazy column goes, I’d love to, but I’m editing a RecyclerView with many ViewHolder types so I’d rather just not do more than I need to right now and risk other type of regressions. Just want to add one more type which happens to be a composable and has some animation inside of it.Stylianos Gakis
10/16/2021, 9:51 AMStylianos Gakis
10/17/2021, 10:13 AMitemAnimator on my recyclerView as null or by setting the supportsChangeAnimations to false on the existing itemAnimator like this: (itemAnimator as? SimpleItemAnimator)?.supportsChangeAnimations = false
But is this what we’re supposed to do? This way I am removing the default change animation for the other non-compose views which I might actually want to have. I absolutely must be missing something super obvious here, it shouldn’t be that hard to achieve what I want.Albert Chang
10/17/2021, 10:37 AMStylianos Gakis
10/17/2021, 10:49 AMAlbert Chang
10/17/2021, 10:54 AMAlbert Chang
10/17/2021, 10:57 AMStylianos Gakis
10/17/2021, 10:58 AMAlbert Chang
10/17/2021, 12:10 PMChris Johnson
10/17/2021, 9:27 PMStylianos Gakis
10/17/2021, 9:39 PMChris Johnson
10/17/2021, 9:46 PMderivedStateOf and remember
That's if you want to convert your items to composables. Using AndroidView should be easy to use. If you have performance problems, first remember on debug builds LazyColumns are a bit slower, and second, that's probably when you wanna look into converting your list items to compose 😅Chris Johnson
10/17/2021, 9:52 PMStylianos Gakis
10/17/2021, 10:12 PMChris Johnson
10/17/2021, 10:15 PMStylianos Gakis
10/17/2021, 10:30 PMChris Johnson
10/17/2021, 11:01 PM