adte
06/03/2023, 12:15 PMAlexander Maryanovsky
06/03/2023, 1:06 PMAlexander Maryanovsky
06/03/2023, 1:06 PMadte
06/03/2023, 5:39 PMYes - BringIntoViewRequesterOh, that's really cool! And is there some api that can give me the Rect coordinates for an item, or I need to calculate that myself?
Alexander Maryanovsky
06/03/2023, 5:43 PMAlexander Maryanovsky
06/03/2023, 5:45 PMadte
06/03/2023, 6:53 PMadte
06/03/2023, 6:54 PMAlexander Maryanovsky
06/03/2023, 6:55 PMAlexander Maryanovsky
06/03/2023, 6:56 PMfirstVisibleItemIndex
and firstVisibleItemScrollOffset
adte
06/03/2023, 6:56 PMAlexander Maryanovsky
06/03/2023, 6:56 PMAlexander Maryanovsky
06/03/2023, 6:56 PMLazyGridLayoutInfo
adte
06/03/2023, 6:57 PMAlexander Maryanovsky
06/03/2023, 8:16 PMadte
06/03/2023, 8:48 PMadte
06/03/2023, 8:51 PMscrollToItem
which is great when you want to e.g. scroll to a letter in Contacts app on a phone (when doing fast scrolling).
For desktop navigating up and down with the keyboard and bringing the item into view is a common use caseAlexander Maryanovsky
06/03/2023, 9:54 PM/**
* Scrolls the minimum distance to make the item at the given index fully visible.
*/
suspend fun LazyListState.scrollToMakeVisible(index: Int) {
val viewportHeight = layoutInfo.viewportEndOffset - layoutInfo.viewportStartOffset
val visibleItemsInfo = layoutInfo.visibleItemsInfo
val firstVisibleItem = visibleItemsInfo.first()
val lastVisibleItem = visibleItemsInfo.last()
if (index <= firstVisibleItem.index) { // The item is before the first visible item
animateScrollToItem(index, 0)
}
else if (index == lastVisibleItem.index){ // The item is the last visible item (it may be partially visible)
// The last visible item is already fully visible, such as when the viewport is taller than the contents
if (lastVisibleItem.offset + lastVisibleItem.size <= viewportHeight)
return
// Walk up until we overshoot the viewport height.
// The item we've reached is the item we need to scroll to, and the offset is the amount we overshoot
var visibleItemIndex = visibleItemsInfo.lastIndex
var distanceToItem = 0
while (distanceToItem < viewportHeight){
distanceToItem += visibleItemsInfo[visibleItemIndex].size
visibleItemIndex -= 1
}
animateScrollToItem(visibleItemsInfo[visibleItemIndex+1].index, distanceToItem - viewportHeight)
}
else if (index == lastVisibleItem.index + 1){ // The item is the first non-visible item below
// We can't accurately align the bottom of the target item with the bottom of the list because we don't know
// the height of its view. Best we can do is assume it's the same as the last visible item's view
var visibleItemIndex = visibleItemsInfo.lastIndex
var distanceToItem = lastVisibleItem.size // <-- Assumption is here
while (distanceToItem < viewportHeight){
distanceToItem += visibleItemsInfo[visibleItemIndex].size
visibleItemIndex -= 1
}
animateScrollToItem(visibleItemsInfo[visibleItemIndex+1].index, distanceToItem - viewportHeight)
}
else if (index > lastVisibleItem.index) {
// We assume all the items are of the same size, which is hopefully true.
// There's nothing better we can do here because the views for these items don't actually exist, so we can't
// know their sizes.
val itemHeight = lastVisibleItem.size
val scrollIndex = index - viewportHeight / itemHeight
val scrollOffset = itemHeight - viewportHeight % itemHeight
animateScrollToItem(scrollIndex, scrollOffset)
}
}
Alexander Maryanovsky
06/03/2023, 9:56 PMadte
06/04/2023, 6:45 AMBut this only works for the simple caseThanks for sharing. Yes, that's why I think eventually there should be an API for this.
adte
06/04/2023, 7:21 AMadte
06/04/2023, 7:28 AMviewportWidth / itemWidth
to get the column count I need to subtract 1 from itemWidth
, because sometimes it could be off, e.g. I get itemWidth=204 and viewportWidth=610, the division then is 2.99, rounds to 2 instead of 3Alexander Maryanovsky
06/04/2023, 7:48 AM