Android Compose 03 基础Layout

前言

  学会基本控件之后,马上着手搭建自己的Compose应用都会遇到下面的情况,为什么控件都全部叠起来了。这时是不是想起了界面构建的另一主要元素。

  Android中界面构建除了组件还有一个最重要的东西——布局,组件实现功能,布局实现界面。布局也是构建前端界面重要的组成部分。在传统的View中,Android提供了许多layout来实现不同的布局,不乏有许多好用的layout例如constrainLayout等。在Compose中,目前稳定的只有一些比较基本的布局,虽然也有类似于constrainLayout的布局但是目前还只是一个测试版,所以使用Compose进行布局的时候需要对基础的布局有更深入的理解。

基本布局介绍

箱子Box(帧布局/FrameLayout)

  FrameLayout作为在View里面也是最经典的布局之一,以左上角为起点,具有堆栈性质越往后的布局,最后添加的控件显示在最上层。在Compose中可以使用Box完成相同的功能。如下是Box布局最简单的用法:

  但是这不是和没有使用布局的情况一模一样吗?在Compose中如果不使用布局文件时默认是一个布满屏幕的Box布局,所以如果单纯使用Box布局的话是与不使用布局是一致的。

行布局Column(纵向线性布局/LinearLayout)

  线性布局也是View中比较常用的一种布局,可以实现控件按照水平或者垂直的顺序显示。在Compose中将线性布局的方向分为了两个布局控件。行布局(Column)控件可以使被包含的控件每一个单独一行显示,即纵向排列,如下是Column布局最简单的用法:

  Column布局也可以两个参数verticalArrangement和horizontalAlignment,这两个参数分别定义了Column布局在纵向的布局方式以及在横向的对齐方式,例如下图实现了一个控件间等距(无边距),水平居中的纵向线性布局:

  • 其中verticalArrangement参数可以传入以下值:
    • Arrangement.Top 对齐顶部
    • Arrangement.Center 居中对齐
    • Arrangement.Bottom 对齐底部
    • Arrangement.SpaceBetween 控件间等距**(无边距)**
    • Arrangement.SpaceAround 控件间等距**(边距为控件间间距一半)**
    • Arrangement.SpaceEvenly 控件间等距**(边距与控件间间距一致)**
  • 其中horizontalAlignment参数可传入以下值:
    • Alignment.Start 对齐头部
    • Alignment.End 对齐尾部
    • Alignment.CenterHorizontally 水平居中

列布局Row(横向线性布局/LinearLayout)

  在Compose中横向布局是使用Row布局控件实现的,与Column布局类似,Row布局可以使包含在其中的控件以每个一列的方式显示即横向显示。如下是Row布局最简单的用法:

  与Column布局类似的Row布局也可以传入两个参数verticalAlignment和horizontalArrangement分别控制纵向的对齐方式以及横向的布局方式,下图实现了一个垂直居中,控件间等距(边距为控件间间距一半)的横向线性布局。

  与Column布局类似可传入的值相同,唯一不同的是在verticalAlignment中居中使用的是Alignment.CenterVertically(垂直居中)

辅助布局控件以及Modifier的使用

辅助布局控件

间隔Spacer

  Compose中不仅仅可以使用传参的方式调整控件间的间距,如果需要单独控制两个控件之间的间距时可以使用Spacer控件自由的控制间隔的大小。Spacer最简单的用法如下,例如实现一个间隔不相同的纵向线性列表。

分割线Divider

  Compose提供了一个分割线的控件Divider,这个控件可以快速的满足需要分割线的情况。Divider控件还可以传入两个参数color和startIndent分别控制分割线的颜色以及分割线的起始偏移,下图在纵向布局中加入了一条起始偏移为100dp的品红色分割线和一条黑色分割线:

  需要值得注意的是,使用Divider控件创建的分割线默认是水平分割线并且宽度是撑满整个屏幕的,如果需要控制Divider的宽度或者创建垂直分割线的话需要配合控件修饰器Modifier来实现

控件修饰器Modifier基本用法

  单纯使用布局或者控件能够实现的界面比较有限,而且对于部分比较复杂的布局实现起来的难度十分大。于是Compose引入了一个修饰器Modifier。Compose自带的控件绝大部分都可以传入一个参数modifier,利用Modifier可以操控布局或者控件的一些基本属性,使布局更加自由以及方便。导入的Modifier是androidx.compose.ui.Modifier包中的,请注意不要使用了错误的包中的Modifier

尺寸控制(height/width/size/fillMaxWidth/fillMaxHeight/aspectRatio)

  Modifier使用最多的是控制控件或者布局的尺寸,使用Modifier.height可以控制控件的高度,使用Modifier.width可以控制控件的宽度。也可以使用Modifier.size(width, height)的方式控制宽高,当使用Modifier.size(size)时表示控件宽高一致。传入的值类型都为DP,在Compose中为Int等数值类型创建了一个拓展方法Int.dp.可以将Int等数值类型转为DP

  如下创建了一个宽度为200dp的横向布局,其中包括了三种不同尺寸的Image:

  对于Image而言如果宽高与图像比例不一致的话,默认会采用最短的一边来显示完整图像即AndroidView中ImageView的fitCenter一致

  特殊地,使用fillMaxWidth/fillMaxHeight可以实现AndroidView中的matchParent的效果,如下图实现了一个横向填满以及一个纵向填满的Image:

  当需要使用特殊宽高比例的大小没有给出完整的宽高数据时,可以使用Modifier.aspectRatio来避免计算的烦恼。Modifier.aspectRatio需要传入的是宽高比即宽除高之后的值,如下图实现了一个宽高比为2的Image:

image-20221122182236314

边距控制(Padding)

  在Compose中的Padding与AndroidView中的Padding不太相同,反而会更接近于Margin。下面更加直观的体现了Padding的用法:

image-20221122172626510
image-20221122173353452

  可以看出不像AndroidView中的Padding会改变Image的大小,反而更加像是使用了Margin添加了外边距。但这只是在Image的父容器没有固定最大尺寸的时候会出现。当Image的父容器出现了给定尺寸时,再次添加Padding显示的效果就和AndroidView中的Padding类似,如下图:

image-20221122181524258

  Padding不仅仅可以传入一个值代表周围的边距值,也可以传入不同个数的参数。

  • Modifier.padding(horizontal, vertical)  分别定义水平方向以及竖直方向的边距值
  • Modifier.padding(start, top, end, bottom) 分别定义不同方向的边界值

切割/圆角(Clip)

  在平常的设计中经常会用到圆角或者圆形的图片,在AndroidView中实现这种功能比较麻烦,一般是使用自定义View的方法去实现。但是这种方法十分麻烦,所以通常是采用第三方的库来实现圆角或者圆形的图片。但是在Compose中Modifier提供了切割(Clip)的功能,通过该功能我们可以很方便的将图片切割成想要的形状,并且Compose中还内置了圆角以及圆形的形状。下图实现了一个四角圆角为10dp的图片以及一个圆形图片:

  对于RoundedCornerShape它不仅仅可以对图片四角同时操作也可以单独控制一个圆角,下图实现了单独操控图片圆角的效果

交互(Clickable)

  在AndroidView中想实现点击View然后执行一些操作的交互方式,可以通过实现其setOnClickListener的方法实现交互。在Compose中只要使用Modifier.Clickable就可以轻松的完成这个操作。特别的,在Compose的预览界面中还可以通过点击预览上的start interactive mode来直接开启交互模式,验证一些简单的交互操作,不需要启动虚拟机十分方便!

image-20221124181612786

  以上就是修饰器Modifier的一些常用的基本用法,当然Modifier的用处远远不止这一些还有许多复杂的用途,例如实现滑动监听等等,这些我们以后在慢慢了解。

上手

  学到这里,应该已经可以完成一些比较简单的界面设计了,下面有一个设计可以尝试着自己实现一下,我也会把源码放在下面。

image-20221124183127208

源码如下:

    Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = Modifier
            .padding(horizontal = 10.dp, vertical = 20.dp)
    ) {
        Image(
            painter = painterResource(id = R.drawable.melon),
            contentDescription = "icon",
            modifier = Modifier
                .size(80.dp)
                .clip(CircleShape)
        )
        Spacer(modifier = Modifier.width(10.dp))
        Column {
            Text(
                text = "Username",
                fontSize = 20.sp,
                color = Color.White
            )
            Spacer(modifier = Modifier.height(10.dp))
            Text(
                text = "Recent message!",
                fontSize = 18.sp,
                color = Color.White
            )
        }
    }

初めて会ったの日から 僕の心の全てを奪った