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

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

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

【问题讨论】:

    标签: android kotlin android-jetpack android-jetpack-compose


    【解决方案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}") }
            )
        }
    

    【讨论】:

    【解决方案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")
                    }
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-10-27
          相关资源
          最近更新 更多