Compose TopAppBar 实现 Android AppBarLayout联动滑动效果
TopAppBarScrollBehavior关联行为,有三种类型。具体如下:
enterAlwaysScrollBehavior:当用户调出 Scaffold 的内部内容时,顶部应用栏会收起。当用户随后将内部内容下拉时,应用栏会展开。exitUntilCollapsedScrollBehavior:与enterAlwaysScrollBehavior类似,不过当用户到达 Scaffold 内部内容的末尾时,应用栏会额外展开。pinnedScrollBehavior:应用栏留在原处,不对滚动操作做出响应。
rememberTopAppBarState scrollBehavior关联顶部区TopAppBar, Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)关联内容区
@Composable
fun CenterAlignedTopAppBarExample() {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
CenterAlignedTopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.primary,
),
title = {
Text(
"Centered Top App Bar",
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
navigationIcon = {
IconButton(onClick = { /* do something */ }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
},
actions = {
IconButton(onClick = { /* do something */ }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "Localized description"
)
}
},
scrollBehavior = scrollBehavior,
)
},
) { innerPadding ->
ScrollContent(innerPadding)
}
}
完整实现
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
private fun ScaffoldPadding() {
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
var selectedItem by remember { mutableIntStateOf(0) }
val items = listOf("Songs", "Artists", "Playlists")
val listState = rememberLazyListState()
var scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(containerColor = Color.Transparent,
snackbarHost = { SnackbarHost(snackbarHostState) }, floatingActionButton = {
var clickCount by remember { mutableStateOf(0) }
ExtendedFloatingActionButton(
onClick = {
// show snackbar as a suspend function
scope.launch {
snackbarHostState.showSnackbar(
"Snackbar # ${++clickCount}"
)
}
}
) { Text("Show snackbar") }
}, topBar = {
TopAppBar(
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(containerColor = Color.Transparent),
title = {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
"Simple TopAppBar",
maxLines = 1,
overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center
)
}
}, navigationIcon = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Localized description"
)
}
}, actions = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "Localized description"
)
}
}, scrollBehavior = scrollBehavior
)
}, bottomBar = {
BottomAppBar(Modifier.height(40.dp)) {
items.forEachIndexed { index, item ->
Column(
Modifier
.weight(1f, true)
.clickable {
selectedItem = index
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
Icons.Filled.Favorite,
contentScale = ContentScale.Inside,
contentDescription = item,
modifier = Modifier.height(16.dp),
colorFilter = if (selectedItem == index) ColorFilter.tint(
MaterialTheme.colorScheme.primary
) else null
)
Text(
item,
fontSize = TextUnit(12f, TextUnitType.Sp),
modifier = Modifier,
color = if (selectedItem == index) MaterialTheme.colorScheme.primary else Color.Unspecified,
fontWeight = if (selectedItem == index) FontWeight.Medium else FontWeight.Normal
)
}
}
}
// NavigationBar(Modifier, windowInsets = WindowInsets(0,0,0,0)) {
// items.forEachIndexed { index, item ->
// NavigationBarItem(
// icon = { Image(Icons.Filled.Favorite, contentScale = ContentScale.Inside ,contentDescription = item, modifier = Modifier.height(16.dp)) },
// label = { Text(item, fontSize = TextUnit(12f, TextUnitType.Sp), modifier = Modifier) },
// selected = selectedItem == index,
// onClick = { selectedItem = index }, modifier = Modifier
// )
// }
// }
}) { innerPadding ->
// innerPadding contains inset information for you to use and apply
LazyColumn(
// consume insets as scaffold doesn't do it by default
modifier = Modifier.consumeWindowInsets(innerPadding).nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = innerPadding,
verticalArrangement = Arrangement.spacedBy(0.5.dp),
state = listState
) {
items(count = 100) {
Box(
Modifier
.fillMaxWidth()
.height(50.dp)
.background(Color.LightGray), contentAlignment = Alignment.Center
) {
Text(text = "Item$it")
}
}
}
}
}