如何在 TopAppBar 的布局中心对齐标题?

How to align title at layout center in TopAppBar?

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 它显示稍微正确。我怎样才能使标题文本位于布局中心。

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

@Composable
fun TopAppBarCompose(){

    TopAppBar(
        title = {
            Box(modifier = Modifier.fillMaxWidth()) {
                Text(
                    text = "Hello",
                    fontSize = 30.sp,
                    modifier = Modifier.align(Alignment.Center)
                )
            }
        },
    )
}

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

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

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

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


我重做了一些本机实现。

只需要做两件事:

1.Add这个文件到你的项目中。这是 TopAppBar class 的稍微修改的实现。 https://gist.github.com/evansgelist/aadcd633e9b160f9f634c16e99ffe163

2.Replace 在您的代码中 TopAppBarCenterTopAppBar。这就是全部!

  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
    )

结果

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

如果你只有标题,那么你可以这样做:

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

如果你的两边都有一个图标,你应该可以做同样的事情,如果一边有两个图标,另一边有两个图标,那么可能需要调整一些东西,然后可能想要使用添加相同大小的图标来均匀地放置东西out 并将 enabled 设置为 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
                    )
                }
            }
        )
    }

如果您使用的是 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
    )
}

我只是 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