可滑动隐藏的 TopAppBar —— Compose
效果:
在原生中非常简单,直接使用 CoordinatorLayout + AppBarLayout 在设置 behavior 就行。
不过在 Compose 中却没有直接实现,这里只能自己实现了。
ScrollableTabRow 和 HorizontalPager
首先先把 Tab 和 Pager 的 可组合函数 封装好:
@OptIn(ExperimentalPagerApi::class)
@Composable
fun HomeTab(
modifier: Modifier = Modifier,
pagerState: PagerState,
count: Int,
label: @Composable (Int, Boolean)->Unit,
){
ScrollableTabRow(
edgePadding = 18.dp,
backgroundColor = MaterialTheme.colors.primary,
divider = {},
modifier = modifier,
indicator = {
TabRowDefaults.Indicator(
color = MaterialTheme.colors.secondary,
modifier = Modifier.pagerTabIndicatorOffset(pagerState, it))
},
selectedTabIndex = pagerState.currentPage) {
repeat(count){
label(it, pagerState.currentPage == it)
}
}
}
@OptIn(ExperimentalPagerApi::class)
@Composable
fun HomePagerLayout(
modifier: Modifier = Modifier,
pagerState: PagerState,
count: Int,
pager: @Composable PagerScope.(Int) -> Unit
){
HorizontalPager(
count = count,
state = pagerState,
modifier = modifier,
content = pager
)
}
BehaviorToolbarScaffold
然后是我封装好的 Scaffold
/**
* Created by HeYanLe on 2022/2/4 19:38.
* https://github.com/heyanLE
*/
/**
* 支持滑动隐藏 Toolbar 的 Scaffold
* @param toolbarSize Toolbar 高度
* @param toolbar Toolbar 可组合函数
* @param content 主体,需要联动滑动的需要指定 NestedScrollConnection
*/
@Composable
fun BehaviorToolbarScaffold (
toolbarSize: Dp = 56.dp,
toolbar: @Composable ()->Unit,
content: @Composable (NestedScrollConnection)-> Unit
){
// TopAppbar 的 offset
val offset = remember {
mutableStateOf(0f)
}
val nestedScrollConn = object: NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// 滑动计算,消费让 TopAppbar 滑动的对应 Offset,同时让 TopAppbar 滑动
var off = (offset.value) + available.y
var con = available.y
if(off >= 0){
off = 0f
con = 0-(offset.value)
}
if(off <= -(toolbarSize.toPx())){
off = -(toolbarSize.toPx())
con = -(toolbarSize.toPx()) - (offset.value)
}
offset.value = off
return Offset(0f, con)
}
}
// 为了避免遮挡,这里需要分两层
Box() {
// 主体层使用一个 Spacer 占位
Column {
Spacer(modifier = Modifier.height(((toolbarSize.toPx()) - (-offset.value)).toDp()))
content(nestedScrollConn)
}
// TopAppbar 层随着 offset 移动
Box(modifier = Modifier
.fillMaxWidth()
.height(toolbarSize)
.graphicsLayer { translationY = (offset.value) }){
toolbar()
}
}
}
组合
然后就是把之前的组合一起:
需要注意的是 需要在 需要联动滑动的 可组合函数的 Modifier 中将 NestedScrollConnection 传入
@OptIn(ExperimentalPagerApi::class)
@Composable
fun TestPreview(){
val pagerState = rememberPagerState()
val scope = rememberCoroutineScope()
Column() {
BehaviorToolbarScaffold(
toolbar = {
TopAppBar(
elevation = 0.dp,
backgroundColor = MaterialTheme.colors.primary,
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
,
title = {
Text(text = stringResource(id = R.string.app_name))
}
)
}
){ conn ->
HomeTab(
modifier = Modifier.height(56.dp),
pagerState = pagerState, count = 2) { i,b ->
Tab(
unselectedContentColor = MaterialTheme.colors.onPrimary,
selectedContentColor = MaterialTheme.colors.secondary,
modifier = Modifier.fillMaxHeight(),
selected = b,
onClick = {
scope.launch {
pagerState.animateScrollToPage(i)
}
}) {
Text(text = "test")
}
}
HomePagerLayout(
modifier = Modifier
.fillMaxSize(),
pagerState = pagerState,
count = 2) {
Box(modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background)){
when(it){
0 -> TestPage(conn)
1 -> TestPage(conn)
}
}
}
}
}
}
LazyColmn
然后是页面:
@Composable
fun TestPage(
nestedScrollConnection: NestedScrollConnection
){
LazyColumn(
modifier = Modifier
.fillMaxWidth()
// 需要将 nestedScrollConnection 传入要联动滑动的 可组合函数的 Modifier 中
.nestedScroll(nestedScrollConnection)
){
items(50){
Text(text = it.toString())
}
}
}