详解Jetpack Compose的标准布局

前言

Compose是一个声明式UI系统,其中,我们用一组函数来声明UI,并且一个Compose函数可以嵌套另一个Compose函数,并以树的结构来构造所需要的UI。

在Compose中,我们称该树为UI 图,当UI需要改变的时候会刷新此UI图,比如Compose函数中有 if 语句,那么Kotlin编译器就需要注意了。

在Compose的世界中,没有类的概念,全都是函数,并且都是顶层函数,因此不会有任何继承和层次机构问题。

Jetpack Compose也是第一个使用Kotlin正在开发中的大型项目,因此Android团队正在探索Kotlin API指南的新世界,以创建一组特定于Compose API的指南。

在这里为大家讲解一下Jetpack Compose中的Column、LazyColumn、Row、LazyRow、Box等,这些标准布局的使用。

Column

Column是垂直布局,让子元素以垂直的方式排序。

简单的例子

代码:

@Preview()
@Composable
fun MyColumn() {
    val spacerModifier = Modifier
        .size(100.dp, 50.dp)
        .padding(5.dp)
        .background(color = Color(0xFF96FF9A))

    val columnModifier = Modifier
        .background(color = Color.White)
        .padding(10.dp)
        .border(width = 1.dp, color = Color.Black, shape = RoundedCornerShape(5.dp))

    Column(modifier = columnModifier) {
        for (i in 0..2){
            Spacer(modifier = spacerModifier)
        }
    }
} 

效果图:

Column 的对齐方式

关键是 horizontalAlignment 与 verticalArrangement

代码:

@Preview(widthDp = 100, heightDp = 200)
@Composable
fun MyColumn() {
    val textModifier = Modifier
        .size(100.dp, 50.dp)
        .padding(5.dp)
        .background(color = Color(0xFF3F51B5))

    Column(
        //让子元素件水平对齐
        //Alignment.CenterHorizontally 为居中对齐,还能使用Start靠左对齐,End靠右对齐
        horizontalAlignment = Alignment.CenterHorizontally,
        //让子元素垂直对齐
        verticalArrangement = Arrangement.Center,
        modifier = Modifier
            .size(150.dp, 300.dp)
            .border(1.dp, color = Color.Black, shape = RectangleShape)
            .background(color = Color.White)
            .padding(10.dp)
    ) {
        for (i in 0..2) {
            Text(
                text = "$i",
                color = Color.White,
                textAlign = TextAlign.Center,
                modifier = textModifier
            )
        }
    }
} 

效果图:

 其他的垂直对齐方式

在官方文档里有一些其他的对齐方式效果,但是实际代码中已经没有了,可能那部分Api是实验性代码,这里只举例存在的。

指定间隔对齐

关键是spacedBy方法。说实话,这个方法提供的有点多余.. padding完全可以满足间隔的需求

代码:

Column(
    horizontalAlignment = Alignment.CenterHorizontally,
    //让子元素垂直对齐   Alignment.Top  Alignment.CenterVertically  Alignment.Bottom
    verticalArrangement = Arrangement.spacedBy(5.dp, alignment = Alignment.CenterVertically),
    modifier = columnModifier
) {
    for (i in 0..2) {
        Text(
            text = "$i",
            color = Color.White,
            textAlign = TextAlign.Center,
            modifier = textModifier.background(color = Color(0xFF004564))
        )
    }
}

效果图:

Column的滚动

代码:

@Preview(widthDp = 100, heightDp = 200)
@Composable
fun MyColumnScroll() {
    val textModifier = Modifier
        .size(100.dp, 50.dp)
        .padding(5.dp)
        .background(color = Color(0xFF3F51B5))

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(color = Color.White)
            .verticalScroll(rememberScrollState())
    ) {
        for (i in 0..50) {
            Text(
                text = "$i",
                color = Color.White,
                textAlign = TextAlign.Center,
                modifier = textModifier
            )
        }
    }
} 

 效果图:

Row

Row是横向布局,让子元素以水平的方式排序

简单的例子

代码:

@Preview(widthDp = 350, heightDp = 100)
@Composable
fun MyRow() {
    val textModifier = Modifier
        .size(100.dp, 50.dp)
        .padding(5.dp)
        .background(color = Color(0xFF3F51B5))

    Row(
        modifier = Modifier
            .fillMaxSize()
            .border(1.dp, color = Color.Black, shape = RectangleShape)
            .background(color = Color.White)
            .padding(10.dp)
    ) {
        for (i in 0..2) {
            Text(
                text = "$i",
                color = Color.White,
                textAlign = TextAlign.Center,
                modifier = textModifier
            )
        }
    }
} 

效果图:

Row 的对齐方式

代码:

@Preview(widthDp = 350, heightDp = 100)
@Composable
fun MyRow() {
    val textModifier = Modifier
        .size(100.dp, 50.dp)
        .background(color = Color(0xFF3F51B5))

    Row(
        //水平对齐
        horizontalArrangement = Arrangement.SpaceBetween,
        //垂直对齐
        verticalAlignment = Alignment.CenterVertically,
        modifier = Modifier
            .fillMaxSize()
            .border(1.dp, color = Color.Black, shape = RectangleShape)
            .background(color = Color.White)
            .padding(5.dp)
    ) {
        for (i in 0..2) {
            Text(
                text = "$i",
                color = Color.White,
                textAlign = TextAlign.Center,
                modifier = textModifier
            )
        }
    }
} 

效果图:

其他对齐方式

LazyColumn 与 LazyRow

LazyColumn 与 LazyRow的使用其实差不多,代码上不同的地方已经在上面展示了。 所以这里归为一类进行代码演示。

   首先你要知道LazyColumn 与 Column 的区别是什么? 区别是 Column 一般是静态的元素数量的列表,而LazyColumn 一般是动态的元素数量的列表(比如可以上拉加载更多的情况)。 当然并不是说Column 无法通过状态管理改造成动态的,只是没必要,因为LazyColumn可以满足需求。  最后你还需要知道LazyColumn的使用必须配合item与items使用。

LazyRow同上描述,不在重复啰嗦。

代码:

@Preview()
@Composable
fun MyColumnScroll() {
    val textModifier = Modifier
        .fillMaxWidth()
        .padding(5.dp)
        .background(color = Color(0xFF3F51B5))

    val list = remember {
        mutableStateListOf<String>("1", "2", "3", "4", "5", "6", "7", "8")
    }

    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .background(color = Color.White)
    ) {

        //注意这里使用的是item
        item {
            TextButton(
                onClick = { list.add("${list.size + 1}") },
                modifier = Modifier
                    .padding(5.dp)
                    .fillMaxWidth()
                    .background(color = Color(0xFF3F51B5))

            ) {
                Text(
                    text = "添加",
                    color = Color.White,
                    fontSize = 18.sp,
                    textAlign = TextAlign.Center,
                    modifier = textModifier
                )
            }
        }

        //注意这里使用的是items
        items(list.size) { index ->
            Text(
                text = "${list[index]}",
                color = Color.White,
                fontSize = 18.sp,
                textAlign = TextAlign.Center,
                modifier = textModifier
            )
        }

        item {
            TextButton(
                onClick = { list.removeRange(list.size - 1, list.size) },
                modifier = Modifier
                    .padding(5.dp)
                    .fillMaxWidth()
                    .background(color = Color(0xFF3F51B5))

            ) {
                Text(
                    text = "删除",
                    color = Color.White,
                    fontSize = 18.sp,
                    textAlign = TextAlign.Center,
                    modifier = textModifier
                )
            }
        }
    }
} 

 效果图:

监听滚动位置 

rememberLazyListState是关键

@Preview()
@Composable
fun MyColumnScroll() {
    val textModifier = Modifier
        .fillMaxWidth()
        .padding(5.dp)
        .background(color = Color(0xFF3F51B5))

    val scrollState = rememberLazyListState()
    val scope = rememberCoroutineScope()

    //scrollState.isScrollInProgress判断是否正在滚动
    if (scrollState.isScrollInProgress){
        DisposableEffect(Unit){
            Log.e("zh", "开始滚动")
            onDispose {
                Log.e("zh", "停止滚动")
                if (scrollState.firstVisibleItemIndex == 0){
                    Toast.makeText(this@MainActivity, "已经到顶了", Toast.LENGTH_SHORT).show()
                    Log.e("zh", "已经到顶了")
                }
                if (scrollState.firstVisibleItemIndex + scrollState.layoutInfo.visibleItemsInfo.size == scrollState.layoutInfo.totalItemsCount){
                    Toast.makeText(this@MainActivity, "已经到底了", Toast.LENGTH_SHORT).show()
                    Log.e("zh", "已经到底了")
                }
            }
        }
    }

    Column {
        Text(
            text = "当前滚动位置 = ${scrollState.firstVisibleItemIndex}",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .fillMaxWidth()
        )
        Text(
            text = "回到顶上",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .fillMaxWidth()
                .clickable {
                    scope.launch {
                        scrollState.animateScrollToItem(0)
                    }
                }
        )
        LazyColumn(
            state = scrollState,
            modifier = Modifier
                .fillMaxSize()
                .background(color = Color.White)
        ) {
            items(list.size) { index ->
                Text(
                    text = "${list[index]}",
                    color = Color.White,
                    fontSize = 18.sp,
                    textAlign = TextAlign.Center,
                    modifier = textModifier
                )
            }
        }
    }
} 

效果图:

Box

Box其实就是相对布局的概念

Box上的属性

代码:

@Preview()
@Composable
fun MyBox() {
    Box(
        //Alignment.TopStart ; Alignment.TopCenter ; Alignment.TopEnd ;
        //Alignment.CenterStart ; Alignment.Center Alignment.CenterEnd ;
        //Alignment.BottomStart ; Alignment.BottomCenter ; Alignment.BottomEnd
        contentAlignment = Alignment.BottomCenter,
        modifier = Modifier
            .background(Color.Gray)
            .fillMaxHeight()
            .fillMaxWidth()
    ) {
        Text(text = "Text", fontSize = 50.sp, color = Color.White)
    }
} 

效果图:

子元素上的属性

@Preview()
@Composable
fun MyBox() {
    Box(
        modifier = Modifier
            .background(Color.Gray)
            .fillMaxHeight()
            .fillMaxWidth()
    ) {
        Text(
            text = "TopStart",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.TopStart)
        )
        Text(
            text = "TopCenter",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.TopCenter)
        )
        Text(
            text = "TopEnd",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.TopEnd)
        )
        Text(
            text = "CenterStart",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.CenterStart)
        )
        Text(
            text = "Center",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.Center)
        )
        Text(
            text = "CenterEnd",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.CenterEnd)
        )
        Text(
            text = "BottomStart",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.BottomStart)
        )
        Text(
            text = "BottomCenter",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.BottomCenter)
        )
        Text(
            text = "BottomEnd",
            fontSize = 18.sp,
            color = Color.White,
            modifier = Modifier
                .background(Color.Black)
                .align(Alignment.BottomEnd)
        )
    }
} 

效果图:

LazyVerticalGrid 与 LazyHorizontalGrid 网格布局

LazyVerticalGrid是竖向网格布局的,实现效果请查看下列代码。而LazyHorizontalGrid是横向网格布局的,只是滚动方向上的不同可以完全参考下面的LazyVerticalGrid。就不在重复举例了

设置目标列数

下面的代码中使用GridCells.Fixed(5)设置了5列

@Preview(widthDp = 1280, heightDp = 720)
@Composable
fun textList() {
    LazyVerticalGrid(columns = GridCells.Fixed(5)) {
        items(100) { index ->
            Text(
                text = index.toString(),
                fontSize = 30.sp,
                textAlign = TextAlign.Center,
                color = Color.Black,
                modifier = Modifier
                    .padding(10.dp)
                    .fillMaxSize()
                    .background(color = Color.LightGray)
            )
        }
    }
}

效果图

根据Item大小自动变化列数

下面代码中使用了GridCells.Adaptive(minSize = 400.dp),将item最小宽度设置成400dp

@Preview(widthDp = 1280, heightDp = 720)
@Composable
fun imageList() {
    val imageList =  remember {
        mutableStateListOf<Int>()
    }
    imageList.add(R.mipmap.ic_bg_1)
    imageList.add(R.mipmap.ic_bg_2)
    imageList.add(R.mipmap.ic_bg_3)
    imageList.add(R.mipmap.ic_bg_3)
    imageList.add(R.mipmap.ic_bg_1)
    imageList.add(R.mipmap.ic_bg_2)
    LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 400.dp)) {
        items(imageList.size) { index ->
            Image(painter = painterResource(id = imageList[index]), contentDescription = null)
        }
    }
}

效果图

猜你喜欢

转载自blog.csdn.net/qq_39312146/article/details/130738154