Jetpack Compose 从入门到入门

Posted 唯鹿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Compose 从入门到入门相关的知识,希望对你有一定的参考价值。

开始布局部分。这部分我个人感觉没有必要每个组件、属性都详细说到,否则篇幅会很长。建立起Compose中的组件与 android Views的一个对应关系就够了。具体还是需要在实际的使用中去熟悉。

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。下图是文档给的各属性示意,很直观。
  • 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。直接上图:
  • 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)
        )
    

预览效果:

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后,预览效果如下:

可以看到原本的红蓝色块因为高度只有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,
        )
    

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

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

  • 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.参考

以上是关于Jetpack Compose 从入门到入门的主要内容,如果未能解决你的问题,请参考以下文章

Jetpack Compose 从入门到入门

Jetpack Compose 从入门到入门

Jetpack Compose 从入门到入门

Jetpack Compose 从入门到入门

Jetpack Compose 从入门到入门

Jetpack Compose 从入门到入门