《Jetpack Compose系列学习》-11 Compose横向线性布局Row和帧布局Box

769 阅读3分钟
Row

横向线性布局和竖向线性布局很像,都属于线性布局,不同的是竖向线性布局在屏幕上垂直排列,而横向线性布局在屏幕上水平排列: image.png

在Android View中,实现横向线性布局也是LinearLayout,只需要修改orientation就可以自由切换横向纵向,但是在Compose中横向和纵向是两个不同的控件,但又有很多相同之处,我们先看一个例子:

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically)
{
    DefaultText("Text1") // 控件1
    DefaultText("Text2") // 控件2
    DefaultText("Text3") // 控件3
}

image.png

我们看到三个Text组件水平排列,并且横向和纵向都居中。同Column一样,我们看看Row的源码:

@Composable
inline fun Row(
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
    verticalAlignment: Alignment.Vertical = Alignment.Top,
    content: @Composable RowScope.() -> Unit
) {
    val measurePolicy = rowMeasurePolicy(horizontalArrangement, verticalAlignment)
    Layout(
        content = { RowScopeInstance.content() },
        measurePolicy = measurePolicy,
        modifier = modifier
    )
}
modifier

Row的修饰符。

horizontalArrangement

布局子控件的水平排列方式。

verticalAlignment

布局子控件的垂直排列方式。

content

Row中子控件。

我们发现Row的参数和Column基本一样,大同小异。我们在Column中学习过Alignment和Arrangement,下面我们看看horizontalArrangement:

@Stable
val Start = object : Horizontal {
    // 省略...
}

@Stable
val End = object : Horizontal {
   // 省略...
}

实现Arrangement。Horizontal接口的只有上面的两个类,剩下的就是前面说过的实现HorizontalOrVertiacal接口的类了,HorizontalOrVertical中也学习了,这里就不说了。 接下来看看verticalAlignment:

@Stable
fun interface Alignment {
    
    @Stable
    fun interface Horizontal {
        // 省略...
    }

    @Stable
    fun interface Vertical {
        // 省略...
    }

    companion object {
        // 2D Alignments.
        @Stable
        val TopStart: Alignment = BiasAlignment(-1f, -1f)
        @Stable
        val TopCenter: Alignment = BiasAlignment(0f, -1f)
        @Stable
        val TopEnd: Alignment = BiasAlignment(1f, -1f)
        @Stable
        val CenterStart: Alignment = BiasAlignment(-1f, 0f)
        @Stable
        val Center: Alignment = BiasAlignment(0f, 0f)
        @Stable
        val CenterEnd: Alignment = BiasAlignment(1f, 0f)
        @Stable
        val BottomStart: Alignment = BiasAlignment(-1f, 1f)
        @Stable
        val BottomCenter: Alignment = BiasAlignment(0f, 1f)
        @Stable
        val BottomEnd: Alignment = BiasAlignment(1f, 1f)

        // 1D Alignment.Verticals.
        @Stable
        val Top: Vertical = BiasAlignment.Vertical(-1f)
        @Stable
        val CenterVertically: Vertical = BiasAlignment.Vertical(0f)
        @Stable
        val Bottom: Vertical = BiasAlignment.Vertical(1f)

        // 1D Alignment.Horizontals.
        @Stable
        val Start: Horizontal = BiasAlignment.Horizontal(-1f)
        @Stable
        val CenterHorizontally: Horizontal = BiasAlignment.Horizontal(0f)
        @Stable
        val End: Horizontal = BiasAlignment.Horizontal(1f)
    }
}

verticalAlignment和Column中的horizontalAlignment也类似。到现在我们线性布局都学完了,下面我们再看看帧布局Box。

Box

Box是帧布局,同Android View中的FrameLayout:

Box {
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        modifier = Modifier.size(200.dp, 200.dp),
        contentDescription = "这是一张图片",
        alignment = Alignment.Center,
        alpha = 1.0f,
        colorFilter = ColorFilter.tint(Color.Red)
    )

    Image(
        painter = painterResource(id = R.drawable.ic_launcher_background),
        modifier = Modifier.size(100.dp, 100.dp),
        contentDescription = "这是一张图片",
        alignment = Alignment.Center,
        alpha = 1.0f,
        colorFilter = ColorFilter.tint(Color.Green)
    )
}

image.png

它可以将控件进行堆叠,后面的子控件直接覆盖在前面的子控件子上,将前面子控件的部分或全部遮挡,如上图。先看看Box的源码:

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -> Unit
) {
    val measurePolicy = rememberBoxMeasurePolicy(contentAlignment, propagateMinConstraints)
    Layout(
        content = { BoxScopeInstance.content() },
        measurePolicy = measurePolicy,
        modifier = modifier
    )
}

可以看到Box也有排列方式,之前Column和Row的排列方式是Alignment.Horizontal或Alignment.Vertical,但是这里显示类型是Alignment,我们看看Alignment接口有哪些:

companion object {
    // 2D Alignments.
    @Stable
    val TopStart: Alignment = BiasAlignment(-1f, -1f)
    @Stable
    val TopCenter: Alignment = BiasAlignment(0f, -1f)
    @Stable
    val TopEnd: Alignment = BiasAlignment(1f, -1f)
    @Stable
    val CenterStart: Alignment = BiasAlignment(-1f, 0f)
    @Stable
    val Center: Alignment = BiasAlignment(0f, 0f)
    @Stable
    val CenterEnd: Alignment = BiasAlignment(1f, 0f)
    @Stable
    val BottomStart: Alignment = BiasAlignment(-1f, 1f)
    @Stable
    val BottomCenter: Alignment = BiasAlignment(0f, 1f)
    @Stable
    val BottomEnd: Alignment = BiasAlignment(1f, 1f)

    // 1D Alignment.Verticals.
    @Stable
    val Top: Vertical = BiasAlignment.Vertical(-1f)
    @Stable
    val CenterVertically: Vertical = BiasAlignment.Vertical(0f)
    @Stable
    val Bottom: Vertical = BiasAlignment.Vertical(1f)

    // 1D Alignment.Horizontals.
    @Stable
    val Start: Horizontal = BiasAlignment.Horizontal(-1f)
    @Stable
    val CenterHorizontally: Horizontal = BiasAlignment.Horizontal(0f)
    @Stable
    val End: Horizontal = BiasAlignment.Horizontal(1f)
}

分别为顶部、中间、结尾,中间开始、中间、结尾,底部开始、中间和结尾。我们下面通过一个例子,来更深入了解下其排列方式:

Column(
    modifier = Modifier.fillMaxSize(),
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally,
) {
    Row {
        Box(
            contentAlignment = Alignment.TopStart,
            modifier = Modifier.size(100.dp).background(Color.Gray),
        ) {
            Text("1", fontSize = 20.sp)
        }
        Box(
            contentAlignment = Alignment.TopCenter,
            modifier = Modifier.size(100.dp).background(Color.Magenta),
        ) {
            Text("2", fontSize = 20.sp)
        }
        Box(
            contentAlignment = Alignment.TopEnd,
            modifier = Modifier.size(100.dp).background(Color.Cyan),
        ) {
            Text("3", fontSize = 20.sp)
        }
    }
    Row {
        Box(
            contentAlignment = Alignment.CenterStart,
            modifier = Modifier.size(100.dp).background(Color.DarkGray),
        ) {
            Text("4", fontSize = 20.sp)
        }
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier.size(100.dp).background(Color.Green),
        ) {
            Text("5", fontSize = 20.sp)
        }
        Box(
            contentAlignment = Alignment.CenterEnd,
            modifier = Modifier.size(100.dp).background(Color.Red),
        ) {
            Text("6", fontSize = 20.sp)
        }
    }

    Row {
        Box(
            contentAlignment = Alignment.BottomStart,
            modifier = Modifier.size(100.dp).background(Color.Magenta),
        ) {
            Text("7", fontSize = 20.sp)
        }
        Box(
            contentAlignment = Alignment.BottomCenter,
            modifier = Modifier.size(100.dp).background(Color.Yellow),
        ) {
            Text("8", fontSize = 20.sp)
        }
        Box(
            contentAlignment = Alignment.BottomEnd,
            modifier = Modifier.size(100.dp).background(Color.Magenta),
        ) {
            Text("9", fontSize = 20.sp)
        }
    }

}

结合了之前学习的Column和Row,运用Box的contentAlignment参数控制其内容的对齐方式,来看看效果:

image.png

结果和我们预期的一致,分别为顶部开始、中间、结尾,中间开始、中间、结尾,底部开始、中间和结尾。大家以后可以根据自己实际的业务需求去运用Box。