I'm trying to custom a Lazy Column to show it like a Wheel of numbers. Depending how far is the item from center I apply opacity, size and rotation to it. When I only apply the opacity, there is no problem, but if I add the size for example, the Lazy Column, when I dragg it, it lags a little bit.
This is the code:
@Composable
fun WheelPicker(
items: List<Any> = (1..100).toList(),
visibleItems: Int = 5,
itemHeight: Dp = 20.dp
) {
val columnHeight = itemHeight*visibleItems
val columnWidth = getMinWidthForList(items)
val paddingValues = ((visibleItems/2) * itemHeight.value).dp
var selection by remember { mutableStateOf(items.first()) }
val scope = rememberCoroutineScope()
val lazyListState = rememberLazyListState()
val offsetViewport = with(LocalDensity.current) { itemHeight.toPx() }
val offsetToDown = offsetViewport * 0.35
val offsetToUp = offsetViewport - (offsetViewport * 0.35)
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.fillMaxWidth(0.6f)
.height(20.dp)
.clip(RoundedCornerShape(5.dp))
.background(Color.LightGray)
)
BoxWithConstraints(
modifier = Modifier
.width(columnWidth)
.height(columnHeight),
contentAlignment = Alignment.Center
) {
LazyColumn(
state = lazyListState,
horizontalAlignment = Alignment.CenterHorizontally,
contentPadding = PaddingValues(top = paddingValues, bottom = paddingValues),
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
) {
itemsIndexed(items) { index, item ->
val opacity by remember {
derivedStateOf {
val currentItemInfo = lazyListState.layoutInfo.visibleItemsInfo
.firstOrNull { it.index == index }
?: return@derivedStateOf 0.2f
val offsetLimit = lazyListState.layoutInfo.viewportEndOffset
(1 - abs(currentItemInfo.offset).toFloat()/offsetLimit)
}
}
val rotation by remember {
derivedStateOf {
val currentItemInfo = lazyListState.layoutInfo.visibleItemsInfo
.firstOrNull { it.index == index }
?: return@derivedStateOf 0.2f
val offsetLimit = lazyListState.layoutInfo.viewportEndOffset
(0f + (abs(currentItemInfo.offset).toFloat()/offsetLimit)*90f)
}
}
val size by remember {
derivedStateOf {
val currentItemInfo = lazyListState.layoutInfo.visibleItemsInfo
.firstOrNull { it.index == index }
?: return@derivedStateOf 0.2f
val offsetLimit = lazyListState.layoutInfo.viewportEndOffset
(20 - (abs(currentItemInfo.offset).toFloat()/offsetLimit)*10)
}
}
Text(
text = item.toString(),
modifier = Modifier
.fillMaxWidth()
.height(size.dp)
.clickable {
scope.launch { lazyListState.animateScrollToItem(index) }
}
.alpha(opacity)
.graphicsLayer {
rotationX = rotation
},
textAlign = TextAlign.End
)
}
}
}
}
LaunchedEffect(lazyListState.isScrollInProgress) {
if (!lazyListState.isScrollInProgress) {
if (lazyListState.firstVisibleItemScrollOffset > offsetToDown) {
lazyListState.scrollToItem(lazyListState.firstVisibleItemIndex+1)
}
if (lazyListState.firstVisibleItemScrollOffset < offsetToUp) {
lazyListState.scrollToItem(lazyListState.firstVisibleItemIndex)
}
selection = items[lazyListState.firstVisibleItemIndex]
}
}
}
Maybe the 3 variables to calculate opacity, size and rotation are causing this behavior?
My other question is, is better to make this with a custom layout? or is this approach good?