【问题标题】:How to align title at layout center in TopAppBar?如何在 TopAppBar 的布局中心对齐标题?
【发布时间】:2022-05-03 05:20:52
【问题描述】:
TopAppBar(
       backgroundColor = Color.Transparent,
       elevation = 0.dp,
       modifier= Modifier.fillMaxWidth(),
       navigationIcon = {
               IconButton(
                   onClick = { TODO },
                   enabled = true,
               ) {
                   Icon(
                       painter = painterResource(id = R.drawable.icon_back_arrow),
                       contentDescription = "Back",
                   )
               }
           }
       },
       title = {
           Text(
               modifier = if (action == null) Modifier.fillMaxWidth() else Modifier,
               textAlign = if (action == null) TextAlign.Center else TextAlign.Start,
               maxLines = 1,
               text = "Hello"
           )
       },
       actions = {
           action?.run {
               Text(
                   modifier = Modifier
                       .padding(horizontal = 16.dp)
                       .clickable(onClick = TODO),
                   color = Color.Green,
                   text ="Cancel",
               )
           }
       } 

我是 jetpack 的新手,如果 action 为 null,我希望将 TopAppBar 的标题对齐。标题未对齐布局中心。当没有 navigationIcon 它工作但添加 navigationIcon 它显示得稍微正确。如何在布局中心制作标题文本。

【问题讨论】:

  • 保持 Text 为 wrap Content 并将其重心设置为相对于 parent 的中心。

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


【解决方案1】:

使用 Material2,您必须使用 TopAppBar 的另一个构造函数,该构造函数没有预定义的内容槽,允许您自定义内部内容的布局。

你可以这样做:

val appBarHorizontalPadding = 4.dp
val titleIconModifier = Modifier.fillMaxHeight()
    .width(72.dp - appBarHorizontalPadding)

TopAppBar(
    backgroundColor = Color.Transparent,
    elevation = 0.dp,
    modifier= Modifier.fillMaxWidth()) {

    //TopAppBar Content
    Box(Modifier.height(32.dp)) {

        //Navigation Icon 
        Row(titleIconModifier, verticalAlignment = Alignment.CenterVertically) {                
            CompositionLocalProvider(
                LocalContentAlpha provides ContentAlpha.high,
            ) {
                IconButton(
                    onClick = { },
                    enabled = true,
                ) {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_add_24px),
                        contentDescription = "Back",
                    )
                }
            }
        }

        //Title
        Row(Modifier.fillMaxSize(),
            verticalAlignment = Alignment.CenterVertically) {

            ProvideTextStyle(value = MaterialTheme.typography.h6) {
                CompositionLocalProvider(
                    LocalContentAlpha provides ContentAlpha.high,
                ){
                    Text(
                        modifier = Modifier.fillMaxWidth(),
                        textAlign = TextAlign.Center,
                        maxLines = 1,
                        text = "Hello"
                    )
                }
            }
        }

        //actions
        CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            Row(
                Modifier.fillMaxHeight(),
                horizontalArrangement = Arrangement.End,
                verticalAlignment = Alignment.CenterVertically,
                content = actions
            )
        }
    }
}


使用Material3,您可以简单地使用CenterAlignedTopAppBar

CenterAlignedTopAppBar(
    title = { Text("Centered TopAppBar") },
    navigationIcon = {
        IconButton(onClick = { /* doSomething() */ }) {
            Icon(
                imageVector = Icons.Filled.Menu,
                contentDescription = "Localized description"
            )
        }
    }
)

【讨论】:

  • 在这种情况下,CompositionLocalProvider 做了什么,为什么它是强制性的?
  • @Gabriele 你能在这里描述一下 CompositionLocalProvider 的目的吗?
  • @DarshAnsari 为此使用撰写主题代码实验室,或参考文档。它只是覆盖了Row里面占用的默认值。默认情况下,一些 Composable 通过内部组合提供者获取它们的值。可以通过使用 CompositionLocalProvider “提供”自定义值(在本例中为 alpha)来覆盖或自定义此行为。
【解决方案2】:

我稍微重做了原生实现。

只需要做两件事:

1.将此文件添加到您的项目中。这是TopAppBar 类的略微修改的实现。 https://gist.github.com/evansgelist/aadcd633e9b160f9f634c16e99ffe163

2.将代码中的 TopAppBar 替换为 CenterTopAppBar。就是这样!

  Scaffold(
        topBar = {
            CenterTopAppBar(
                title = {
                    Text(
                        textAlign = TextAlign.Center,
                        text = text,
                    )
                },

编辑 扩展代码

val Number.toPx
    get() = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        this.toFloat(),
        Resources.getSystem().displayMetrics
    )

结果

【讨论】:

  • 无法导入import com.klikatech.rollo.ui.extensions.common.toPx
  • 将此代码添加到我的主要答案中
【解决方案3】:

如果您使用的是 Material3,您也可以使用 CenterAlignedTopAppBar

fun CenterAlignedTopAppBar(
    title: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    navigationIcon: @Composable () -> Unit = {},
    actions: @Composable RowScope.() -> Unit = {},
    colors: TopAppBarColors = TopAppBarDefaults.centerAlignedTopAppBarColors(),
    scrollBehavior: TopAppBarScrollBehavior? = null
) {
    SingleRowTopAppBar(
        modifier = modifier,
        title = title,
        titleTextStyle =
        MaterialTheme.typography.fromToken(TopAppBarSmallTokens.HeadlineFont),
        centeredTitle = true,
        navigationIcon = navigationIcon,
        actions = actions,
        colors = colors,
        scrollBehavior = scrollBehavior
    )
}

【讨论】:

    【解决方案4】:

    标题居中的核心是左右占用槽的空间相同。您只需要调整插槽的默认大小。我们给左右槽默认占用空间的槽,可以很好的解决这个问题,代码也很简单。

    @Composable
    fun TopBar(title: Int, actions: @Composable (() -> Unit)? = null, popOnClick: () -> Unit) {
    
        val modifier = Modifier.size(width = 70.dp, height = 50.dp).background(Color.Red)
    
        TopAppBar(
            title = {
                Text(text = stringResource(id = title), fontSize = 16.sp,
                 textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()) },
            navigationIcon = {
                Box(modifier = modifier, contentAlignment = Alignment.Center) {
                    IconButton(onClick = { popOnClick() }) {
                        Icon(Icons.Filled.ArrowBack, contentDescription = "", tint = MaterialTheme.colors.primary)
                    }
                }
            },
            actions = {
                Box(modifier = modifier, contentAlignment = Alignment.Center) {
                    if (actions != null) {
                        actions()
                    }
                }
            },
            backgroundColor = MaterialTheme.colors.surface,
            elevation = 1.dp
        )
    }
    
    
    

    【讨论】:

      【解决方案5】:
      @Composable
      fun TopAppBarCompose(){
      
          TopAppBar(
              title = {
                  Box(modifier = Modifier.fillMaxWidth()) {
                      Text(
                          text = "Hello",
                          fontSize = 30.sp,
                          modifier = Modifier.align(Alignment.Center)
                      )
                  }
              },
          )
      }
      

      【讨论】:

      • 添加导航图标后效果不佳,然后它将在图标和组件末尾之间居中
      【解决方案6】:

      这取决于应用栏中的内容。

      如果您只有标题,那么您可以执行以下操作:

      topBar = {
              TopAppBar(content = {
                  Text(
                      modifier = Modifier.fillMaxWidth(),
                      text = "Title Text",
                      textAlign = TextAlign.Center,
                      style = MaterialTheme.typography.h6,
                  )
              })
          },
      

      如果您在任一侧都有一个图标,您应该能够做同样的事情,如果一侧和另一侧有两个图标,则可能需要调整一些东西,然后可能想要使用添加相同大小的图标来平衡事情将 enable 设置为 false 以使其不可点击并且将颜色设置为透明因此看不到,否则您可以尝试计算大小并在文本中添加填充结尾,看来您还需要为透明图标添加 16.dp 填充,因为导航图标在标题之前有额外的填充,而不是在标题和操作之间

      这是我为箭头图标和标题所做的

       topBar = {
              TopAppBar(
                  navigationIcon = {
                      IconButton(
                          onClick = {
                              navController.popBackStack()
                          }
                      ) {
                          Icon(
                              imageVector = Icons.Default.ArrowBack,
                              contentDescription = "back arrow icon"
                          )
                      }
                  },
                  title = {
                      Text(
                          modifier = Modifier
                              .fillMaxWidth(),
                          text = "Basic Navigation",
                          textAlign = TextAlign.Center,
                      )
                  },
                  actions = {
                      IconButton(
                           modifier = Modifier
                              .padding(start = 16.dp),
                          enabled = false,
                          onClick = {}
                      ) {
                          Icon(
                              imageVector = Icons.Default.ArrowBack,
                              contentDescription = "back arrow icon",
                              tint = Color.Transparent
                          )
                      }
                  }
              )
          }
      

      【讨论】:

        【解决方案7】:

        我只是 Jetpack Compose 的初学者,在寻找该问题的解决方案之前,我试图自己想办法,也许这对某人来说已经足够了。我需要 TopAppBar 的居中标题,仅在左侧带有导航图标,或者在左侧和右侧带有两个图标,这个解决方案对我来说还可以。后来我将其配置为在右侧包含传递的图标或不包含。

                TopAppBar(
                    backgroundColor = Color.Green,
                    elevation = 5.dp,
                ) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                        verticalAlignment = Alignment.CenterVertically
                    ) {
                        // size of an icon and placeholder box on the right side
                        val iconSize = 32.dp
        
                        Icon(
                            imageVector = Icons.Default.ArrowBack,
                            contentDescription = "Arrow Back",
                            modifier = Modifier
                                .clickable {
                                    navController.popBackStack()
                                }
                                .size(iconSize)
                        )
                        Text(text = "Centered Title", fontSize = 32.sp)
                        // placeholder on the right side in order to have centered title - might be replaced with icon
                        Box(
                            modifier = Modifier
                                .size(iconSize)
                        ) { }
                    }
        

        我无法附上截图,预览在这里:https://i.stack.imgur.com/UNQTF.png

        【讨论】:

          【解决方案8】:

          以前的解决方案太复杂了。其实很简单:

          title = {
              Text(
                  text = "title",
                  textAlign = TextAlign.Center,
                  modifier = Modifier.fillMaxWidth()
              )
          }
          

          【讨论】:

          • 它会在开始时留下一些空间
          • 正是我发现的:文本开头的空格
          • 在 16 dp 的修饰符中添加一个结束填充,然后文本组件将具有 16 dp 的水平间距和完全居中对齐的标题。
          猜你喜欢
          • 2017-05-28
          • 1970-01-01
          • 1970-01-01
          • 2019-10-20
          • 2017-01-20
          • 1970-01-01
          • 2019-07-15
          • 2020-10-10
          • 1970-01-01
          相关资源
          最近更新 更多