效果

实现
@Composable
fun WheelPicker(
count: Int,
onCheckedChange: (Int) -> Unit,
modifier: Modifier = Modifier,
showCount: Float = 5f,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = contentColorFor(backgroundColor),
child: @Composable BoxScope.(index: Int) -> Unit,
) {
BoxWithConstraints(modifier = modifier.background(backgroundColor)) {
val density = LocalDensity.current
val height = density.run { maxHeight.toPx() }
val itemHeight = height / showCount
val state = rememberLazyListState(
initialFirstVisibleItemIndex = Int.MAX_VALUE / 2 - Int.MAX_VALUE / 2 % count,
initialFirstVisibleItemScrollOffset = ((itemHeight - height) / 2).roundToInt(),
)
LaunchedEffect(Unit) {
snapshotFlow {
state.isScrollInProgress
}.filterNot { it }.onEach {
(state.firstVisibleItemIndex + state.layoutInfo.visibleItemsInfo.size / 2).let {
onCheckedChange.invoke(it % count)
}
}.launchIn(this)
}
CompositionLocalProvider(LocalContentColor provides contentColor) {
LazyColumn(
modifier = modifier.fillMaxSize(),
state = state,
flingBehavior = rememberSnapFlingBehavior(state),
) {
items(Int.MAX_VALUE) { index ->
Box(
modifier = Modifier
.fillMaxWidth()
.height(maxHeight / showCount),
contentAlignment = Alignment.Center,
) {
child(index = index % count)
}
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.verticalGradient(
colors = listOf(backgroundColor, Color.Transparent, backgroundColor),
),
)
)
}
}
使用
WheelPicker(
count = 47,
onCheckedChange = {
"onCheckedChange - $it".let(::println)
},
modifier = Modifier
.fillMaxWidth()
.height(300.dp),
) {
Text(text = "ITEM_$it")
}