Compose TopAppBar 实现 Android AppBarLayout联动滑动效果

974 阅读1分钟

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")
                    }
                }
            }
        }
    }

效果展示,全屏参考沉浸式 状态栏文章介绍

BI8XKFFY7XKZ49PR_report 00_00_00-00_00_30~1.gif

官方地址