前言
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)
}
}
}
效果图