阅读 808

初识 Jetpack Compose(二) :布局

compose-landing-roadmap.svg

目录

一、布局

官网对于Compose布局的描述

可组合函数是 Compose 的基本构建块。可组合函数是一种发出 Unit 的函数,用于描述界面中的某一部分。该函数接受一些输入并生成屏幕上显示的内容。如需详细了解可组合项,请参阅 Compose 构思模型文档。

一个可组合函数可能会发出多个界面元素。不过,如果您未提供有关如何排列这些元素的指导,Compose 可能会以您不喜欢的方式排列它们。

当我们尝试着像如下编写代码时

@Composable
fun MainPage() {
    Text(text = "Hello,Jetpack Compose!")
    Text(text = "hello,jetpack compose!")
}
复制代码

在预览视图中会发现两个Text重叠了

1630392259(1).jpg 在xml中,我们可以用各种布局方式来排列/约束视图元素的位置,那么在Compose 中如何实现呢?Compose中有没有类似于LinearLayoutFrameLaoyoutConstraintLayout 的东西呢?

答案是:必然有。

在许多情况下,您只需使用 Compose 的标准布局元素即可。

通过以下几种布局方式,基本可以满足日常的开发布局需求。

1. Row

Row可以理解为Xml布局中LinearLayouthorizontal模式,例如:

@Composable
fun MainPage() {
    Row(){
        Text(text = "Hello")
        Text(text = ",")
        Text(text = "jetpack compose!")
    }
}
复制代码

1630395322(1).jpg

通过Row的源码可以看到我们还可以传递如下几个参数:

@Composable
inline fun Row(
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    verticalAlignment: Alignment.Vertical = Alignment.Top,
    content: @Composable RowScope.() -> Unit
) {
    ...
}
复制代码
参数类型默认值含义
modifierModifierModifier布局的修饰符
horizontalArrangementArrangement.HorizontalArrangement.Start布局子级的 水平 放置方式,默认从布局开始往布局结束方向放置
verticalAlignmentAlignment.VerticalAlignment.Top布局子级的 垂直 对其方式,默认从布局顶部对齐

例如: 1630396946(1).jpg

需要注意的是,和LinearLayout一样,超出布局设置的最大宽度或高度的视图将不可见

2. Column

Column可以理解为Xml布局中LinearLayoutvertical模式,例如:

@Composable
fun MainPage() {
    Column(Modifier.padding(Dp(50f))) {
        Text(text = "Hello")
        Text(text = ",")
        Text(text = "jetpack compose!")
    }
}
复制代码

通过Column的源码可以看到我们还可以传递如下几个参数:

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    content: @Composable ColumnScope.() -> Unit
) {
    ...
}
复制代码
参数类型默认值含义
modifierModifierModifier布局的修饰符
verticalArrangementArrangement.VerticalArrangement.Top布局子级的 竖直 放置方式,默认从布局顶部往布局底部方向放置
horizontalAlignmentAlignment.HorizontalAlignment.Start布局子级的 水平 对其方式,默认从布局开始对齐

Column有和Row同样的问题,注意用于修饰Column子级放置和对其方式和Row的参数是不一样的

3. Box

Box可以理解为Xml布局中FrameLayout,例如:

@Composable
fun MainPage() {
    Box(Modifier.size(Dp(300f), Dp(150f)),
        contentAlignment = Alignment.Center) {
        Box(modifier = Modifier
            .background(MaterialTheme.colors.error)
            .size(Dp(200f), Dp(100f)))
        Box(modifier = Modifier
            .background(MaterialTheme.colors.secondary)
            .size(Dp(100f), Dp(50f)))
        Text(text = "hello,jetpack compose!")
    }
}
复制代码

1630400177.jpg

通过Column的源码可以看到我们还可以传递如下几个参数:

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -> Unit
) {
   ...
}
复制代码
参数类型默认值含义
modifierModifierModifier布局的修饰符
contentAlignmentAlignmentAlignment.TopStartBox 内的默认对齐方式
propagateMinConstraintsBooleanfalse传入的最小约束是否应传递给内容。

4. BoxWithConstraints

如需了解来自父项的约束条件并相应地设计布局,您可以使用 BoxWithConstraints。您可以在内容 lambda 的作用域内找到测量约束条件

BoxWithConstraintsBox 使用方式完全一致,只是如官方所说,可以测量约束条件,例如:

@Composable
fun MainPage1() {
    BoxWithConstraints {
        Text("My minHeight is $maxHeight while my maxWidth is $maxWidth")
    }
}
复制代码

在其作用域内可以拿到 BoxWithConstraints的最大最小宽高信息。

5. ConstraintLayout

ConstraintLayout 其实就是Xml布局中的 ConstraintLayout,我们来看官方的描述

ConstraintLayout 有助于根据可组合项的相对位置将它们放置在屏幕上,它是使用多个嵌套 RowColumnBox 和自定义布局元素的替代方案。在实现对齐要求比较复杂的较大布局时,ConstraintLayout 很有用。

注意:在 View 系统中,建议使用 ConstraintLayout 来创建复杂的大型布局,因为扁平视图层次结构比嵌套视图的性能更好。不过,这在 Compose 中不是什么问题,因为它能够高效地处理较深的布局层次结构。

注意:是否将 ConstraintLayout 用于 Compose 中的特定界面取决于开发者的偏好。在 Android View 系统中,使用 ConstraintLayout 作为构建更高性能布局的一种方法,但这在 Compose 中并不是问题。在需要进行选择时,请考虑 ConstraintLayout 是否有助于提高可组合项的可读性和可维护性。

可以看出,虽然官方提供了ConstraintLayout,但并不推荐在Compose中使用,xml中使用ConstraintLayout的根本目的是为了减少视图嵌套层级从而提升android的页面绘制性能,在Compose中不存在视图嵌套这一概念,所以是否使用ConstraintLayout,需要考虑个人习惯和 ConstraintLayout 是否有助于提高可组合项的可读性和可维护性。

在使用ConstraintLayout前,需要在 build.gradle 中添加以下依赖项:

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha08"
复制代码

Compose 中的 ConstraintLayout 支持 DSL

  • 引用是使用 createRefs() 或 createRefFor() 创建的,ConstraintLayout 中的每个可组合项都需要有与之关联的引用。
  • 约束条件是使用 constrainAs() 修饰符提供的,该修饰符将引用作为参数,可让您在主体 lambda 中指定其约束条件。
  • 约束条件是使用 linkTo() 或其他有用的方法指定的。
  • parent 是一个现有的引用,可用于指定对 ConstraintLayout 可组合项本身的约束条件。

依旧看下实例:

@Composable
fun MainPage() {
    ConstraintLayout(Modifier.size(200.dp)) {
        val (button, text) = createRefs()
        Button(
            onClick = { },
            modifier = Modifier.constrainAs(button) {
                top.linkTo(parent.top)
                start.linkTo(parent.start)
                end.linkTo(parent.end)
                bottom.linkTo(parent.bottom)
            },
            colors = ButtonDefaults.buttonColors(
                backgroundColor = MaterialTheme.colors.secondary,
                contentColor = Color.White
            )
        ) {
            Text("Button")
        }

        Text("Text", Modifier.constrainAs(text) {
            top.linkTo(button.top)
        })
    }
}
复制代码

1630404675(1).jpg

可以看出,用法和xml中基本一致,代码中button被约束到视图中心,text的顶部对齐button的顶部。

6. Spacer

一个空白的视图,官方并未提供类似于Xml中margin的属性,可能推荐用这个替代吧。例如:

@Composable
fun MainPage() {
    Row {
        Box(Modifier.size(100.dp).background(Color.Red))
        Spacer(Modifier.width(20.dp))
        Box(Modifier.size(100.dp).background(Color.Magenta))
        Spacer(Modifier.weight(1f))
        Box(Modifier.size(100.dp).background(Color.Black))
    }
}
复制代码

1630405034(1).jpg

二、最后

好记性不如烂笔头,初识 Jetpack Compose 系列是我自己的学习笔记,在加深知识巩固的同时,也可以锻炼一下写作技能。文章中的内容仅作参考,如有问题请留言指正。

1. 参考

2.滚动视图

上面说到 RowColumn 有如果子视图超出父视图边界,则无法展示的问题;在xml中一般使用ScrollView以支持视图滚动,相应的,在Compose中可以由LazyRowLazyColumn实现,但他俩的功能不仅仅只是用来实现视图滚动,所以在本文就不展开了,LazyRowLazyColumn预计将会有后续的widget篇中具体描述。

文章分类
Android