实现LazyColumn滚动时仅首项可见才显示TopAppBar
自定义TopAppBar滚动行为:仅当LazyColumn首项可见时显示
我基于Android Developers的示例实现了一个TopAppBar,原本逻辑是LazyColumn向下滚动时TopAppBar消失,向上滚动时显示。之后我在列表上方添加了一个Box组件,功能正常,但希望调整滚动行为:仅当LazyColumn的第一个列表项(内容为'0')重新可见时,TopAppBar才显示出来,而不是只要向上滚动就触发显示。
原实现代码
@OptIn(ExperimentalMaterial3Api::class) @Preview @Composable fun TopAppBarSample() { val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopAppBar( title = { Text( "TopAppBar", maxLines = 1, overflow = TextOverflow.Ellipsis ) }, navigationIcon = { IconButton(onClick = { /* doSomething() */ }) { Icon( imageVector = Icons.Filled.Menu, contentDescription = "Localized description" ) } }, actions = { // RowScope here, so these icons will be placed horizontally IconButton(onClick = { /* doSomething() */ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = "Localized description" ) } IconButton(onClick = { /* doSomething() */ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = "Localized description" ) } }, scrollBehavior = scrollBehavior ) }, content = { innerPadding -> Column(modifier = Modifier.padding(paddingValues = innerPadding)) { Box( modifier = Modifier .size(200.dp) .background(Color.Gray) ) { } LazyColumn( contentPadding = innerPadding, verticalArrangement = Arrangement.spacedBy(8.dp) ) { val list = (0..75).map { it.toString() } items(count = list.size) { Text( text = list[it], style = MaterialTheme.typography.bodyLarge, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) ) } } } } ) }
修改后的实现方案
核心思路是通过LazyListState跟踪LazyColumn的滚动状态,仅当首项完全可见时才触发TopAppBar的显示:
@OptIn(ExperimentalMaterial3Api::class) @Preview @Composable fun TopAppBarSample() { val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val lazyListState = rememberLazyListState() val scope = rememberCoroutineScope() // 监听首项可见状态,控制TopAppBar显示 LaunchedEffect(lazyListState) { snapshotFlow { // 判断首项是否完全可见:索引为0且滚动偏移量为0 lazyListState.firstVisibleItemIndex == 0 && lazyListState.firstVisibleItemScrollOffset == 0 }.collect { isFirstItemVisible -> if (isFirstItemVisible) { // 平滑滚动TopAppBar到完全显示状态 scope.launch { scrollBehavior.state.animateScrollTo(0) } } } } Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopAppBar( title = { Text( "TopAppBar", maxLines = 1, overflow = TextOverflow.Ellipsis ) }, navigationIcon = { IconButton(onClick = { /* doSomething() */ }) { Icon( imageVector = Icons.Filled.Menu, contentDescription = "菜单" ) } }, actions = { IconButton(onClick = { /* doSomething() */ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = "收藏" ) } IconButton(onClick = { /* doSomething() */ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = "收藏" ) } }, scrollBehavior = scrollBehavior ) }, content = { innerPadding -> Column(modifier = Modifier.padding(paddingValues = innerPadding)) { Box( modifier = Modifier .size(200.dp) .background(Color.Gray) ) LazyColumn( state = lazyListState, // 绑定LazyListState contentPadding = innerPadding, verticalArrangement = Arrangement.spacedBy(8.dp) ) { val list = (0..75).map { it.toString() } items(count = list.size) { Text( text = list[it], style = MaterialTheme.typography.bodyLarge, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) ) } } } } ) }
关键修改说明
- 绑定LazyListState:给LazyColumn添加
state = lazyListState,用于跟踪列表的滚动位置和可见项状态 - 监听首项可见性:通过
snapshotFlow监听lazyListState的变化,当firstVisibleItemIndex为0且firstVisibleItemScrollOffset为0时,判定首项完全可见 - 控制TopAppBar显示:当首项完全可见时,通过
scrollBehavior.state.animateScrollTo(0)让TopAppBar平滑滚动到完全显示状态;向下滚动隐藏的逻辑保持原有行为不变
内容的提问来源于stack exchange,提问作者Kinyo356
相关产品推荐
相关产品推荐

