Jetpack Compose入门基础全面精讲

来自:网络
时间:2022-12-26
阅读:
目录

1. Column

子元素按竖直顺序排列,相当于竖直方向的LinearLayout

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,
    content: @Composable ColumnScope.() -> Unit
)
  • modifier是修饰符,我们放到下一篇详细说明。
  • verticalArrangement,指定子元素在Column中的排列方式,默认是Top。下图是文档给的各属性示意,很直观。

Jetpack Compose入门基础全面精讲

  • horizontalAlignment,指定水平方向的对齐方式,有StartCenterHorizontallyEnd三种,默认Start。这部分和我们的android:gravity属性类似,这里是通过两个属性分开配置。
  • content,就是我们的子元素,用大括号包住。
@Composable
fun ArtistCard() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

2. Row

子元素按水平顺序排列,相当于水平方向的LinearLayout。基本用法与Column一致,简单说明一下。

@Composable
inline fun Row(
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    verticalAlignment: Alignment.Vertical = Alignment.Top,
    content: @Composable RowScope.() -> Unit
)

horizontalArrangement,指定子元素在水平方向的排列方式,默认是Start。直接上图:

Jetpack Compose入门基础全面精讲

verticalAlignment,指定垂直方向的对齐方式,有TopCenterVerticallyBottom三种,默认Top

3. Box

box就像盒子一样,里面的东西可以层层摆放。大体相当于FrameLayout

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -> Unit
)

contentAlignment,指定子元素的对齐方式,八个方向加一个正中九种位置,默认是左上角(LTR)。这个属性我个人觉得使用频率不高,主要还是需要单独去指定各个子元素位置(使用Modifieralign方法)。

这里可以看个文档中的例子:

	Box {
        Box(Modifier.fillMaxSize().background(Color.Cyan))
        Box(
            Modifier.matchParentSize()
                .padding(top = 20.dp, bottom = 20.dp)
                .background(Color.Yellow)
        )
        Box(
            Modifier.matchParentSize()
                .padding(40.dp)
                .background(Color.Magenta)
        )
        Box(
            Modifier.align(Alignment.Center)
                .size(300.dp, 300.dp)
                .background(Color.Green)
        )
        Box(
            Modifier.align(Alignment.TopStart)
                .size(150.dp, 150.dp)
                .background(Color.Red)
        )
        Box(
            Modifier.align(Alignment.BottomEnd)
                .size(150.dp, 150.dp)
                .background(Color.Blue)
        )
    }

预览效果:

Jetpack Compose入门基础全面精讲

Box中的各个子Box从底部向上叠加。通过align指定位置,通过sizepadding调整大小。matchParentSize类似match_parent属性,宽高填充满父布局。

注意:这个子元素的Box和父元素Box虽然长得一样,但实际不是一个组件。前者类似于View,不能添加子View,可以指定大小样式,而后者类似ViewGroup。

propagateMinConstraints,子元素是否使用指定的最小约束,默认false。这个属性直接这么解释很抽象,我们可以接着用上面的例子,添加下面的代码:

	Box(
        Modifier.sizeIn(100.dp, 200.dp),
        propagateMinConstraints = true
    ) {
    ...
    }

我们指定子元素最小宽是100dp,高是200dp后,预览效果如下:

Jetpack Compose入门基础全面精讲

可以看到原本的红蓝色块因为高度只有150dp,所以被约束为了最小的200dp,变成的长方形。

4. BoxWithConstraints

BoxWithConstraints和上面的Box很相似,唯一不同是它多了约束。我们先看源码:

@Composable
fun BoxWithConstraints(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxWithConstraintsScope.() -> Unit
)

注意到不同处是BoxWithConstraintsScope,它继承自BoxScopeBoxScope就是提供了上面使用到的alignmatchParentSize方法的作用域。

/**
 * Receiver scope being used by the children parameter of [BoxWithConstraints]
 */
@Stable
interface BoxWithConstraintsScope : BoxScope {
    /**
     * The constraints given by the parent layout in pixels.
     *
     * Use [minWidth], [maxWidth], [minHeight] or [maxHeight] if you need value in [Dp].
     */
    val constraints: Constraints
    /**
     * The minimum width in [Dp].
     *
     * @see constraints for the values in pixels.
     */
    val minWidth: Dp
    /**
     * The maximum width in [Dp].
     *
     * @see constraints for the values in pixels.
     */
    val maxWidth: Dp
    /**
     * The minimum height in [Dp].
     *
     * @see constraints for the values in pixels.
     */
    val minHeight: Dp
    /**
     * The maximum height in [Dp].
     *
     * @see constraints for the values in pixels.
     */
    val maxHeight: Dp
}

所以BoxWithConstraints不同就是在Box的基础上多了最大最小宽度高度的属性。可以用它来做一些页面适配之类的工作,使用例子如下:

	BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(/* ... */)
                Title(/* ... */)
            }
        } else {
            Row {
                Column {
                    Title(/* ... */)
                    Description(/* ... */)
                }
                Image(/* ... */)
            }
        }
    }

5. ConstraintLayout

使用 Android View 系统时,在嵌套某些 View(如 RelativeLayout)时,可能会出现一些性能问题。由于 Compose 可以避免多次测量,因此可以根据需要进行深层次嵌套,而不会影响性能。

虽然不用考虑嵌套带来的性能问题,但是这写起来一层套一层的也挺闹心的。加上ConstraintLayout我个人已经非常习惯使用了,所以也很希望在Compose中也能使用到它。个人感觉ConstraintLayout 可以提高可读性。

使用Compose中的ConstraintLayout需要额外添加依赖:

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-rc02"

@Composable
inline fun ConstraintLayout(
    modifier: Modifier = Modifier,
    optimizationLevel: Int = Optimizer.OPTIMIZATION_STANDARD,
    crossinline content: @Composable ConstraintLayoutScope.() -> Unit
)

optimizationLevellayout_optimizationLevel一样,用来约束优化的。默认OPTIMIZATION_STANDARD,只优化直接约束和barrier约束。通常我们不需要修改它。

这里用官方的一个例子简单说明一下它的使用方法:

	ConstraintLayout(
        modifier = Modifier
            .fillMaxSize()
    ) {
        val (image, header, tag1, tag2, tag3, bSignup, bLogin, disclaimer) = createRefs()
        val g1 = createGuidelineFromStart(44.dp)
        val g2 = createGuidelineFromEnd(44.dp)
        Image(
            modifier = Modifier.constrainAs(image) {
                width = Dimension.value(201.dp)
                height = Dimension.value(179.dp)
                top.linkTo(parent.top, 32.dp)
                start.linkTo(g1)
            },
            painter = painterResource(id = R.drawable.intercom_snooze),
            contentDescription = null
        )
        Text(
            modifier = Modifier.constrainAs(header) {
                top.linkTo(image.bottom, 32.dp)
                start.linkTo(g1)
                end.linkTo(g2)
                width = Dimension.fillToConstraints
            },
            text = stringResource(id = R.string.welcome_header),
            style = MaterialTheme.typography.h5,
        )
        Text(
            modifier = Modifier.constrainAs(tag1) {
                top.linkTo(header.bottom, 16.dp)
                start.linkTo(g1)
                end.linkTo(g2)
                width = Dimension.fillToConstraints
            },
            text = stringResource(id = R.string.welcome_tagline1)
        )
        Text(
            modifier = Modifier.constrainAs(tag2) {
                top.linkTo(tag1.bottom, 8.dp)
                start.linkTo(g1)
                end.linkTo(g2)
                width = Dimension.fillToConstraints
            },
            text = stringResource(id = R.string.welcome_tagline2)
        )
        Text(
            modifier = Modifier.constrainAs(tag3) {
                top.linkTo(tag2.bottom, 8.dp)
                start.linkTo(g1)
                end.linkTo(g2)
                width = Dimension.fillToConstraints
            },
            text = stringResource(id = R.string.welcome_tagline3)
        )
        Button(
            modifier = Modifier.constrainAs(bSignup) {
                bottom.linkTo(bLogin.top, 16.dp)
                start.linkTo(g1)
                end.linkTo(g2)
                width = Dimension.fillToConstraints
            },
            onClick = {}
        ) {
            Text(text = stringResource(id = R.string.sign_up))
        }
        Button(
            modifier = Modifier.constrainAs(bLogin) {
                bottom.linkTo(disclaimer.top, 16.dp)
                start.linkTo(g1)
                end.linkTo(g2)
                width = Dimension.fillToConstraints
            },
            onClick = {},
        ) {
            Text(text = stringResource(id = R.string.log_in))
        }
        Text(
            modifier = Modifier.constrainAs(disclaimer) {
                bottom.linkTo(parent.bottom, 8.dp)
                start.linkTo(g1)
                end.linkTo(g2)
                width = Dimension.fillToConstraints
            },
            text = stringResource(id = R.string.trial_disclaimer),
            style = MaterialTheme.typography.caption,
        )
    }

预览效果如下(包括约束效果):

Jetpack Compose入门基础全面精讲

Jetpack Compose入门基础全面精讲

说明一下代码中的属性和方法:

  • createRefs()是创建引用。或者说定义需要使用的id。
  • Modifier.constrainAs是定义约束条件。括号内填写开始创建的引用。类似android:id="@+id/xxx"
  • createGuidelineFromXXX就是创建一个Guideline,例子中创建了左右两个Guideline作为左右两边间距参考线。
  • linkTo是用来指定约束条件的。例如top.linkTo(image.bottom, 32.dp) 相当于 app:layout_constraintTop_toBottomOf="@+id/image"android:layout_marginTop="32dp"
  • Dimension.fillToConstraints,填充满约束,类似宽高指定0dp。

当然还有许多的属性方法没有用到,也就不详细的介绍了,有兴趣的可以看官方demo

到此,基础布局篇结束,下一篇详细介绍 Modifier修饰符。

6. 参考

Compose 布局基础知识

Compose 的标准布局元素

返回顶部
顶部