Jetpack Compose滑动按钮菜单实现:中间按钮放大高亮需求
Jetpack Compose 实现中间高亮缩放的滑动按钮菜单
以下是实现需求的完整方案,核心思路是通过跟踪滚动状态计算每个按钮与屏幕中心的距离,动态调整缩放比例和颜色亮度,实现平滑的视觉过渡效果:
完整代码示例
import androidx.compose.animation.core.lerp import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.ShoppingCart import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.launch // 菜单数据类 data class MenuItem(val icon: androidx.compose.ui.graphics.vector.ImageVector, val label: String) @Composable fun CenteredHighlightScrollMenu() { // 模拟菜单数据 val menuItems = listOf( MenuItem(Icons.Filled.Home, "首页"), MenuItem(Icons.Filled.Search, "搜索"), MenuItem(Icons.Filled.Favorite, "收藏"), MenuItem(Icons.Filled.ShoppingCart, "购物车"), MenuItem(Icons.Filled.Person, "我的") ) val configuration = LocalConfiguration.current val screenWidth = configuration.screenWidthDp.dp val screenCenter = screenWidth / 2 val density = LocalDensity.current val scrollState = rememberLazyListState() val coroutineScope = rememberCoroutineScope() LazyRow( state = scrollState, horizontalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues(horizontal = 32.dp), modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { items(menuItems) { item -> val index = menuItems.indexOf(item) var itemCenter by remember { mutableStateOf(0.dp) } // 计算当前按钮与屏幕中心的距离 val distance = with(density) { kotlin.math.abs(itemCenter - screenCenter) } // 控制过渡效果的参数 val maxTransitionDistance = 100.dp // 超过此距离后不再变化 val normalizedDistance = (distance / maxTransitionDistance).coerceAtMost(1f) // 动态计算缩放比例:中心按钮1.2倍,边缘按钮0.8倍 val scale = lerp(1.2f, 0.8f, normalizedDistance) // 动态计算颜色:中心按钮原色,边缘按钮灰色 val textColor = lerp(Color.Black, Color.Gray, normalizedDistance) val iconColorFilter = ColorFilter.tint(lerp(Color.Black, Color.Gray, normalizedDistance), BlendMode.SrcIn) // 动态计算字体大小 val textSize = lerp(14.sp, 12.sp, normalizedDistance) Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier // 获取按钮在屏幕中的位置 .onGloballyPositioned { coordinates -> itemCenter = with(density) { coordinates.localToWindow(coordinates.size.center).x.toDp() } } .scale(scale) .clickable { // 点击按钮时滚动到屏幕中心 coroutineScope.launch { val targetItem = scrollState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == index } targetItem?.let { val offset = scrollState.layoutInfo.viewportSize.width / 2 - it.size.width / 2 scrollState.animateScrollToItem(index, offset) } } } .padding(vertical = 12.dp) ) { Image( painter = rememberVectorPainter(image = item.icon), contentDescription = item.label, modifier = Modifier.size(28.dp), colorFilter = iconColorFilter ) Text( text = item.label, fontSize = textSize, color = textColor, modifier = Modifier.padding(top = 4.dp) ) } } } }
关键实现细节
- 滚动状态跟踪:使用
rememberLazyListState监听滚动位置,配合onGloballyPositioned获取每个按钮的屏幕坐标,计算与屏幕中心的距离。 - 平滑过渡效果:通过
lerp函数实现缩放比例、颜色、字体大小的线性插值,让按钮从边缘到中心的视觉变化更自然。 - 点击交互:点击按钮时调用
animateScrollToItem将目标按钮平滑滚动到屏幕中心,同时触发视觉变化。 - 参数可调:可以通过修改
maxTransitionDistance、缩放比例范围、颜色值等参数,适配不同的UI设计需求。
内容的提问来源于stack exchange,提问作者bovietvidai
相关产品推荐
相关产品推荐

