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