Compose for Android

jetpack compose实战——TopAppBar的使用和

2022-05-04  本文已影响0人  Peakmain

前言

TopAppBar的使用

源码

有两个源码,我们看其中一个

@Composable
fun TopAppBar(
    title: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    navigationIcon: @Composable (() -> Unit)? = null,
    actions: @Composable RowScope.() -> Unit = {},
    backgroundColor: Color = MaterialTheme.colors.primarySurface,
    contentColor: Color = contentColorFor(backgroundColor),
    elevation: Dp = AppBarDefaults.TopAppBarElevation
) 
image.png
       Scaffold(topBar = {
           TopAppBar(title = {
               Text(text = "我是系统的TopAppBar")
           },navigationIcon = {
               Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null)
           },actions = {
               Icon(imageVector = Icons.Default.Add, contentDescription = null)
           })
       }) 

上面我们看的是参数源码,我们看下方法体的源码

fun TopAppBar(
...
) {
    AppBar(
        backgroundColor,
        contentColor,
        elevation,
        AppBarDefaults.ContentPadding,
        RectangleShape,
        modifier
    ) {
        if (navigationIcon == null) {//👈🏻①
            Spacer(TitleInsetWithoutIcon)
        } else {
            //👈🏻②
            Row(TitleIconModifier, verticalAlignment = Alignment.CenterVertically) {
                CompositionLocalProvider(
                    LocalContentAlpha provides ContentAlpha.high,
                    content = navigationIcon
                )
            }
        }

        Row(
       //注释③
            Modifier.fillMaxHeight().weight(1f),
            verticalAlignment = Alignment.CenterVertically
        ) {
            ProvideTextStyle(value = MaterialTheme.typography.h6) {
                CompositionLocalProvider(
                    LocalContentAlpha provides ContentAlpha.high,
                    content = title
                )
            }
        }
      //注释④
        CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            Row(
                Modifier.fillMaxHeight(),
                horizontalArrangement = Arrangement.End,
                verticalAlignment = Alignment.CenterVertically,
                content = actions
            )
        }
    }
}
private val AppBarHorizontalPadding = 4.dp
private val TitleInsetWithoutIcon = Modifier.width(16.dp - AppBarHorizontalPadding)

我们可以看到,如果左边没有图标,那么它会设置一个12dp的空白区域

tips:Spacer空白区域,通过 modifier 设置空白区域的大小

private val AppBarHorizontalPadding = 4.dp
private val TitleIconModifier = Modifier.fillMaxHeight()
    .width(72.dp - AppBarHorizontalPadding)

设置naviationIcon的大小为68.dp

所以代码其实等价于

     Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(56.dp)
                    .background(Color_149EE7)
                    .padding(4.dp),
                horizontalArrangement = Arrangement.SpaceBetween
            ) {
                Row(modifier = Modifier
                    .fillMaxHeight()
                    .width(68.dp)
                    .background(Color.Red),
                    verticalAlignment = Alignment.CenterVertically) {
                    CompositionLocalProvider(
                        LocalContentAlpha provides ContentAlpha.high,
                        content = {
                            Icon(imageVector = Icons.Default.ArrowBack,
                                contentDescription = null,
                                tint = Color.White)
                        }
                    )
                }
                Row(
                    Modifier
                        .fillMaxHeight()
                        .weight(1f),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    ProvideTextStyle(value = MaterialTheme.typography.h6) {
                        CompositionLocalProvider(
                            LocalContentAlpha provides ContentAlpha.high,
                            content = {
                                Text(text = "我是自定义的TopAppBar", color = Color.White)
                            }
                        )
                    }
                }
                CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
                    Row(
                        Modifier.fillMaxHeight(),
                        horizontalArrangement = Arrangement.End,
                        verticalAlignment = Alignment.CenterVertically,
                        content = {
                            Icon(
                                imageVector = Icons.Default.Add,
                                contentDescription = null,
                                modifier = Modifier.background(Color.Blue),
                                tint = Color.White
                            )
                        }
                    )
                }
          }
image.png

存在的问题

ConstraintLayout的使用

ConstraintLayout 可以让组件相对屏幕或其他同级组件进行布局,减少 Row、Column、Box 布局的互相嵌套。
想要在项目中使用ConstraintLayout,必须在build.gradle 中增加依赖,我的放在library中

  api "androidx.constraintlayout:constraintlayout-compose:1.0.0"

用法:constraintlayout使用有两种方式,我这里主要介绍一种方式,大家可以自行查询你第二种方式

 val constraintSet = ConstraintSet {
     val ceshi1 = createRefFor("ceshi1")
     val ceshi2 = createRefFor("ceshi2")
     constrain(ceshi1) {
         start.linkTo(parent.start)
         end.linkTo(parent.end)
         top.linkTo(parent.top)
         bottom.linkTo(parent.bottom)
     }
     constrain(ceshi2) {
         top.linkTo(parent.top)
         bottom.linkTo(parent.bottom)
     }
 }
 ConstraintLayout(constraintSet,
     Modifier
         .height(56.dp)
         .background(Color.Red)
         .fillMaxWidth()) {
     Text(
         text = "测试11",
         modifier = Modifier.layoutId("ceshi1"),
         color = Color.White
     )
     Text(
         text = "测试12",
         modifier = Modifier.layoutId("ceshi2"),
         color = Color.White
     )
}

效果如图


image.png

TopAppBar的封装

结合上面分析,我们的需要注意几点:
1.TopAppBar的高度是56dp
2.TopAppBar的两边的间距是4dp

直接贴代码

@Composable
fun TopAppBarCenter(
    title: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    navigationIcon: @Composable (() -> Unit)? = null,
    backgroundColor: Color = MaterialTheme.colors.primarySurface,
    actions: @Composable RowScope.() -> Unit = {},
    content: @Composable (PaddingValues) -> Unit
) {
   Scaffold(topBar = {
       val constraintSet = ConstraintSet {
           val titleRef = createRefFor("title")
           val navigationIconRef = createRefFor("navigationIcon")
           val actionsRef = createRefFor("actions")
           constrain(titleRef) {
               start.linkTo(parent.start)
               end.linkTo(parent.end)
               top.linkTo(parent.top)
               bottom.linkTo(parent.bottom)
           }
           constrain(navigationIconRef) {
               top.linkTo(parent.top)
               bottom.linkTo(parent.bottom)
           }
           constrain(actionsRef){
               top.linkTo(parent.top)
               bottom.linkTo(parent.bottom)
               end.linkTo(parent.end)
           }
       }
       ConstraintLayout(constraintSet,
           modifier = Modifier
               .fillMaxWidth()
               .background(backgroundColor)
               .height(TopAppBarHeight)
               .then(modifier)) {
           Box(
               Modifier
                   .layoutId("title")
                   .padding(horizontal = 4.dp)
           ) {
               ProvideTextStyle(value = MaterialTheme.typography.h6) {
                   CompositionLocalProvider(
                       LocalContentAlpha provides ContentAlpha.high,
                       content = title
                   )
               }
           }
           if (navigationIcon != null) {
               Box(modifier = Modifier
                   .layoutId("navigationIcon")
                   .padding(start = 4.dp)) {
                   CompositionLocalProvider(
                       LocalContentAlpha provides ContentAlpha.high,
                       content = navigationIcon
                   )
               }
           }
           Row(
               Modifier.layoutId("actions").padding(end = 4.dp),
               content = actions
           )

       }
   }) {
        content(it)
   }

}

使用就非常简单了

    TopAppBarCenter(title = {
        Text(text = "封装首页", color = Color.White)
    }, navigationIcon = {
        Icon(imageVector = Icons.Default.ArrowBack,
            contentDescription = null,
            tint = Color.White)
    }, actions = {
        Icon(imageVector = Icons.Default.Add, contentDescription = null, tint = Color.White)
    }) {
        //绘制内容区
        Text(text = "我是首页")
    }
沉浸式状态栏
//MainActivity
//设置沉浸式状态栏
WindowCompat.setDecorFitsSystemWindows(window, false)
    def accompanist_version = "0.24.7-alpha"
    api "com.google.accompanist:accompanist-systemuicontroller:${accompanist_version}"
        val systemUiController = rememberSystemUiController()
        SideEffect {
            systemUiController.setSystemBarsColor(
                color = Color.Transparent,
                darkIcons = darkIcons
            )
        }
//获取状态栏的高度
 with(LocalContext.current) {
     statusBarHeight =
         resources.getDimensionPixelSize(resources.getIdentifier("status_bar_height",
             "dimen",
             "android"))
 }
 with(LocalDensity.current) {
     statusBarHeightDp = statusBarHeight.toDp()
 }
      ConstraintLayout(constraintSet, modifier = Modifier
            .fillMaxWidth()
            .background(backgroundColor)
            .height(topAppBarHeight + statusBarHeightDp)
            .padding(top = statusBarHeightDp) //设置padding(top=状态栏的高度)
            .then(modifier))

效果如下


image.png

但是当我们把虚拟导航栏打开,我们再看看效果

image.png
我们我们发现下方的BottomNavigation被挡住了,怎么解决呢?这时候我们需要用到另一个Google的库, Insets for Jetpack Compose
    api "com.google.accompanist:accompanist-insets:$accompanist_version"
  BottomNavigation(backgroundColor = MaterialTheme.colors.surface
                , modifier = Modifier.navigationBarsPadding()

运行起来看下效果


image.png

总结

我们首先对TopAppBar的源码进行分析,我们发现原有的TopAppBar并不能满足我们的需求,于是我们自己重写了一个TopAppBar,使用的技术其实也很简单,主要就是使用到了ConstraintLayout。关于沉浸式呢,我们用的都是Google自带的库,用起来也是非常简单。
至此,我们的TopAppBar的封装已经结束了,下篇将介绍Banner图的使用和封装。
最后贴下TopAppBar的源码地址TopAppBar.kt

上一篇下一篇

猜你喜欢

热点阅读