When observing `lazyListState.layoutInfo.visibleIt...
# compose
u
When observing
lazyListState.layoutInfo.visibleItemsInfo
, it’s only forward-looking like
Copy code
///////// first visible index=3 o=448
item= index=3 offset=-448 height=589
item= index=4 offset=141 height=571
item= index=5 offset=712 height=153
granted, it
firstVisibleItemIndex
starts from 0, so, I could cache these height values, to then calculate actual scroll position, which is what I’m after Question is, where would I cache such values? something to the effect of
Copy code
val info = listState.layoutInfo.visibleItemsInfo
val cache = Array(size = info.size) { 0 }
info.forEach {
    cache[it.index] = it.size
}
would a simple
remember
be enough?
a
For what reason are you trying to calculate the absolute scroll position (of a lazy container)? In general, that isn’t possible to know. If you’ve started in the middle of an extremely large data set, where every item could have an arbitrary size, they only way to know the absolute scroll position would be to calculate the height for everything in the list which isn’t feasible
u
I know, I think I only need it working atleast when it starts from 0 The reason is to do the “synchronized scrolling” technique, say you have a “hero” over the list, and you want to scale it, where the scale is a function of current scroll position
or better example, if you have Pixel, in system settings, the search bar, it gets scrolled with the list, but then “clamped” on top which is basically just
searchBar.translationY = scrollPosition.coerceIn(0, 500)
for example the issue is, if the list has say very short items, then I’d only see
listState.firstVisibleItemIndex
0..50, then again 0..50, etc. not 0..1000 (or whatever the visible total height is) tldr; firstVisibleItemIndex gets reset, which would cause my translationY to reset to 0 and it would “jump”, which is not good
and the 1000 I could live with, I dont need to know the total height, which as you said would mean to measure everything, which defeats the purpose of lazylist
a
I think I only need it working at least when it starts from 0
What happens if you scroll the list partway, and then rotate? You then could be starting from the middle of the list, with new sizes.
and the 1000 I could live with
Maybe you could get away with having the first set of items be non-lazy? So the first “item” from the lazy list perspective is actually a few items in a
Column
. So something like:
Copy code
item {
     Column {
          // Items 1-4
     }
}
items {
    // Items 5-the rest
}
You’d probably have to tune the amount of actual items you render at the top in your “special” item. If you don’t do enough, you’d have the same issue now, and if you do too many, you’d be defeating the point of a
LazyColumn
.
For the settings/collapsing app bar case, you might want to go down the nested scrolling route instead
u
could I maybe calculate it from the last visibile item? I mean when I print the
listState.visibleInfo
then I see the last offset being 1519, which plus item height, is like 1.5 “screens” of height; which should be fine, so, 0-1519 you think is impossible? I mean the cache I proposed would work
just donw know where to “put” the cache
nested scrolling? ellaborate please
take a look at this from settings
the “lightness” of the toolbar is clearly interpolated from list scroll position and nested scroll here wouldn’t do
okay maybe this is not the greatest of examples as this could be workedaround by adding a tall “empty” first item and just follow that, and if firstvisibleIndex is 1 then you know its hidden and then you know its supposed to be light
— I’m going to tell you my exact usecase, maybe something comes to mind I have this sort of layout , where there is a colored rectangle underneat the top of the list, but as you can see, its like 1,5 items tall and when the list scrolls, I need to scroll the rectangle “away” as well
a
Compose Material 3 can achieve that settings behavior built in with
exitUntilCollapsedScrollBehavior
, which I think uses the nested scrolling as its implementation
u
lets leave the nested scroll for now please, I don’t think its optimal in this case
the issue with just following
firstVisibleItemOffset
in the orange google music example is it would reset to 0 when the “Its Tuesday+Play music for” item would be scrolled invisible
maybe I could just follow the index of 3rd item and substract the previous heights?
a
I’m not sure why nested scroll wouldn’t work in that case, if the “It’s Tuesday morning” and the background is outside the scrolling container, and the scrolling container starts with the first row of items
u
isn’t nested scrolling less optimal performance wise? when it was first introduced by the appcompat libraries I didn’t like it because it broke the fling, you’d have to do 2 flings to get to the bottom of the list
okay let me reframe the question, if visible info is like this
Copy code
D/Default: item= index=0 offset=-41 height=222
D/Default: item= index=1 offset=181 height=563
D/Default: item= index=2 offset=744 height=766
D/Default: item= index=3 offset=1510 height=589
and my use case would be fine with having scroll position within 0..1510 then, I’d just follow the offset of the index=3 and the question is, where would I cache the initial offset of the 1510, so I then can substract it and get the actual scroll position?
Copy code
val y = if (listState.layoutInfo.visibleItemsInfo.isNotEmpty()) {
    val initial = remember { listState.indexOffset(3) }
    val current = listState.indexOffset(3)
    initial - current
} else {
    0
}
Copy code
private fun LazyListState.indexOffset(index: Int): Int {
    return layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }?.offset ?: 0
}
this sort of works, see any problems with it?
a
I think that could break if index
3
isn’t available, like if you’re scrolled further down the list. For performance concerns, this is one of the primary use cases for nested scrolling so I wouldn’t be concerned there, and I was just trying out the sample of the collapsing top app bar and didn’t see the 2 flings issue.
u
yea 3 ist just a example, I’d put a last visible index there if I were to generalize my question mostly being if it’s okay to cache it with remember
@Alex Vanyo would you please have a example of the nested scrolling? (say the settings search bar we mentioned)
a
Hmm, you might run into edge cases with caching if items change size. The docs for
LargeTopAppBar
have an example using the `exitUntilCollapsedScrollBehavior`: https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#LargeTopAppBar(kotlin.Funct[…]al3.TopAppBarScrollBehavior)
u
thx
so the idea is that I’d only observe the parent scroll and do my interpolations based on that?
563 Views