Jetpack Compose 折叠工具栏

Posted

技术标签:

【中文标题】Jetpack Compose 折叠工具栏【英文标题】:Jetpack Compose collapsing toolbar 【发布时间】:2021-07-17 12:45:58 【问题描述】:

我找不到任何关于此事的文档,是否有类似于 Compose 中的 CollapsingToolbar 的内容?

我发现只是提到了它here,但没有关于如何设置它

【问题讨论】:

【参考方案1】:

我发现了一个由 Samir Basnet(来自 Kotlin Slack Channel)创建的解决方案,它对我很有用,我希望它可以帮助其他人......

@Composable
fun CollapsingEffectScreen() 
    val items = (1..100).map  "Item $it" 
    val lazyListState = rememberLazyListState()
    var scrolledY = 0f
    var previousOffset = 0
    LazyColumn(
        Modifier.fillMaxSize(),
        lazyListState,
    ) 
        item 
            Image(
                painter = painterResource(id = R.drawable.recife),
                contentDescription = null,
                contentScale = ContentScale.FillWidth,
                modifier = Modifier
                    .graphicsLayer 
                        scrolledY += lazyListState.firstVisibleItemScrollOffset - previousOffset
                        translationY = scrolledY * 0.5f
                        previousOffset = lazyListState.firstVisibleItemScrollOffset
                    
                    .height(240.dp)
                    .fillMaxWidth()
            )
        
        items(items) 
            Text(
                text = it,
                Modifier
                    .background(Color.White)
                    .fillMaxWidth()
                    .padding(8.dp)
            )
        
    

结果如下:

【讨论】:

如果有人需要,这里是 slack 消息链接:kotlinlang.slack.com/archives/CJLTWPH7S/p1617182855437800 如果有人需要,这里是 slack 消息链接:kotlinlang.slack.com/archives/CJLTWPH7S/p1617182855437800 此解决方案很有帮助,并且与 android Doc 的折叠工具栏示例不同,因为它仅允许在列表位于顶部时展开折叠的项目。谢谢你的回答 它是否也从顶部图像滚动? 如果内容是混合的,即原生 xml 中的顶部图像(或工具栏)和 jetpack compose 中的剩余布局(LazyColumn)怎么办??【参考方案2】:

我在 Android 文档中发现了这一点,我认为您在问题中链接的文档是在谈论使用嵌套滚动进行这样的操作。

val toolbarHeight = 48.dp
    val toolbarHeightPx = with(LocalDensity.current)  toolbarHeight.roundToPx().toFloat() 
    val toolbarOffsetHeightPx = remember  mutableStateOf(0f) 

    val nestedScrollConnection = remember 
        object : NestedScrollConnection 
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset 

                val delta = available.y
                val newOffset = toolbarOffsetHeightPx.value + delta
                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
                return Offset.Zero
            
        
    
    Box(
        Modifier
            .fillMaxSize()

            .nestedScroll(nestedScrollConnection)
    ) 

        LazyColumn(contentPadding = PaddingValues(top = toolbarHeight)) 
            items(100)  index ->
                Text("I'm item $index", modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp))
            
        
        TopAppBar(
            modifier = Modifier
                .height(toolbarHeight)
                .offset  IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) ,
            title =  Text("toolbar offset is $toolbarOffsetHeightPx.value") 
        )
    

【讨论】:

此解决方案将在列表向上或向下滚动时折叠和展开工具栏。如果您希望工具栏仅在列表滚动到顶部时展开,请参阅@nglauber 答案 感谢这个例子。作为记录,可以在这里找到:developer.android.com/reference/kotlin/androidx/compose/ui/…【参考方案3】:

您可以使用compose-collapsing-toolbar 库。

安装implementation "me.onebone:toolbar-compose:2.1.0"

Usage - Exemple

预览

以下是库的 Readme.md 中的一些 gif 图像:

【讨论】:

【参考方案4】:

在 Jetpack Compose 中,我通过以下代码实现了折叠工具栏:

@Composable
fun MainScreen()
    val scrollState = rememberScrollState()
    // parallax effect by offset
    val imageOffset = (-scrollState.value * 0.18f).dp
    Box 
        Image(
            painter = painterResource(id = R.drawable.bg_header),
            contentDescription = null,
            contentScale = ContentScale.FillWidth,
            modifier = Modifier
                .graphicsLayer  translationY = imageOffset 
                .height(240.dp)
                .fillMaxWidth()
        )

        Column(
            Modifier
                .verticalScroll(scrollState)
                .padding(top = 200.dp)
                .background(
                    MaterialTheme.colors.surface,
                    RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
                )
                .fillMaxHeight()
                .fillMaxWidth()
                .padding(all = 16.dp)
        ) 
            // Main content here
        
    

你可以用 LazyColumn 替换 Column 和val scrollState = rememberLazyListState()

【讨论】:

最好使用.graphicsLayer translationY = -scrollState.value * 0.18f 而不是.offset(y = imageOffset),因为这样您可以更改translationY 而不会触发重组和重新布局。 感谢@GabrieleMariotti。我刚刚更新了这个答案! @WilsonTran 在LazyColumn 的情况下如何计算这个val imageOffset = (-scrollState.value * 0.18f).dp,如何使用rememberLazyListState 获取当前滚动位置值?【参考方案5】:

您可以关注example in the docs 创建一个工具栏,该工具栏在每次向上/向下滚动时都会展开/折叠。

要创建一个仅在列表滚动到顶部时展开的工具栏,您可以对原始示例稍作修改:

val toolbarHeight = 48.dp
val toolbarHeightPx = with(LocalDensity.current)  toolbarHeight.roundToPx().toFloat() 
var toolbarOffsetHeightPx by remember  mutableStateOf(0f) 
var totalScrollOffsetPx = remember  0f 

val nestedScrollConnection = remember 
    object : NestedScrollConnection 
        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset 

            val delta = available.y
            totalScrollOffsetPx += delta
            
            if (totalScrollOffsetPx in -toolbarHeightPx..0f) 
                toolbarOffsetHeightPx = totalScrollOffsetPx
            

            return Offset.Zero
        
    

通过这样做,您可以灵活地创建自己的CollapsibleScaffold,它可以接受scrollBehaviourappBarLayoutlist 可组合等参数。

这样,例如,您还可以以编程方式计算应用栏的高度并摆脱大量样板文件,从而使屏幕中使用的代码整洁干净。

【讨论】:

【参考方案6】:

撰写折叠工​​具栏 Jetpack Compose 的 CollapsingToolbarLayout 的简单实现

https://github.com/onebone/compose-collapsing-toolbar

【讨论】:

虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review【参考方案7】:

Material Design 3 的 Jetpack Compose 实现包括 4 种类型的 Top App Bars (https://m3.material.io/components/top-app-bar/implementation):

CenterAlignedTopAppBar SmallTopAppBar MediumTopAppBar LargeTopAppBar

https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary

它们都有一个scrollBehavior 参数,可用于折叠工具栏。库中有 3 种基本类型的滚动行为:

TopAppBarDefaults.pinnedScrollBehavior TopAppBarDefaults.enterAlwaysScrollBehavior TopAppBarDefaults.exitUntilCollapsedScrollBehavior

https://developer.android.com/reference/kotlin/androidx/compose/material3/TopAppBarDefaults

注意:此 API 目前被注释为实验性的。

示例用法:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Test() 
    val scrollBehavior = remember  TopAppBarDefaults.enterAlwaysScrollBehavior() 
    Scaffold(
        modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
        topBar = 
            MediumTopAppBar(
                title =  Text(text = "Scroll Behavior Test") ,
                navigationIcon = 
                    IconButton(onClick =  /*TODO*/ ) 
                        Icon(imageVector = Icons.Default.Menu, contentDescription = "")
                    
                ,
                scrollBehavior = scrollBehavior
            )
        
    ) 
        LazyColumn(modifier = Modifier.fillMaxWidth()) 
            items((1..50).toList())  item ->
                Text(modifier = Modifier.padding(8.dp), text = "Item $item")
            
        
    

【讨论】:

以上是关于Jetpack Compose 折叠工具栏的主要内容,如果未能解决你的问题,请参考以下文章

有礼品哦!全新界面工具包 Jetpack Compose 使用情况调研

Jetpack Compose 1.0 正式发布!打造原生 UI 的 Android 现代工具包

Jetpack Compose学习 之 HelloWorld

Jetpack Compose学习 之 HelloWorld

深度解析 Jetpack Compose 布局

移动开发 Jetpack Compose 组件布局