赞
踩
第四章 如何在Compose UI中使用ConstraintLayout
hello,各位看官,距离上次更新compose ui教程的时间已经过去了一年,是的,整整一年。主要还是工作的原因抽不出太多时间来写教程,同时因为工作的原因,也还是优先使用自己熟练的命令式UI来进行编码,所谓实践出真知,那么没有实际使用,也就不会来更新教程。
大家在使用命令式UI的时候,就有这种需求,当一个页面布局足够复杂的时候,使用常规的LinearLayout 或RelativeLayout等布局很难实现需求,或者就是需要嵌套很多层,而因为命令式UI的设计问题,当嵌套层级过多的时候,就会引发绘制效率低下的问题,此时ConstraintLayout的出现,就能满足我们实现复杂布局而不用过多嵌套的问题。
前几章我们介绍了官方自带的compose库中,所自带的Row、Column、Box布局,因为compose ui的特性,即使再多的嵌套,也不会引发绘制效率低下的问题,但如果我们有一个复杂的布局,仍需要嵌套多层,此时是不利于代码可读性的,考虑到这点,官方还是研发了ConstraintLayout,截止本文发布,目前可用的稳定版本仍然还是22年5月20日发布的1.0.1版本,最新的alpha版本是1.1.0-alpha13,但已经有大半年都没更新了,原本想等着1.1.0版本发布,看是否有新特性再来介绍的,看来一时半会等不到了
了解如何在compose ui中使用约束布局,以及常见问题
友情提醒,如果各位看官有不懂的代码可以先看一下之前的章节,循序渐进,如果还是有不懂的,可以给我留言
compose版本的ConstraintLayout 和 命令式UI一样,都不在自带的android库中,需要额外引入,目前稳定版本是1.0.1,需要在gradle文件中引入:
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
在命令式UI中,我们需要给在ConstraintLayout 中有相互作用的控件设置id,然后通过app:layout_constraint 来设置左上右下的具体位置,在compose中一样适用
BaseTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ConstraintLayout{
val ivMask = createRef() //这里就是创建一个ID的意思,用于标记
Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
Modifier
.constrainAs(ivMask) { //这里应用上这个ID
//下面这行对应xml中的app:layout_constraintTop_toTopOf="parent"
//但很友好的,官方还将margin的参数也在这里一并传入了,可以点进去看一下
//其实它有3个参数,第二个是margin,第三个是当布局隐藏时的margin
top.linkTo(parent.top, 26.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
.width(356.5.dp)
.height(49.dp))
//有了上面这个ID之后,接下来如果有布局需要根据上面的图片进行位置的调整,就好办了
Text(
text = getString(R.string.start_device_hint),
//当如果你这个控件不会被别的控件所依赖,就可以直接在这里写createRef()
Modifier.constrainAs(createRef()) {
//等同于app:layout_constraintTop_toTopOf="@id/iv_mask"
top.linkTo(ivMask.top)
bottom.linkTo(ivMask.bottom)
start.linkTo(ivMask.start)
end.linkTo(ivMask.end)
}, color = Color.White, fontSize = 14.sp, textAlign = TextAlign.Center
)
}
}
}
假设上文还有更多的布局,下一个布局也需要依赖Text的位置,那么就需要给Text也设置一个ID,当然你可以再多写一行代码,如:
val tv_text = createRef()
然后将这个tv_text应用到上文的Text中去,但同时你也可以这么写:
val (ivMask,tv_text) = createRefs()
这样你就可以创建多个ID,用逗号分隔即可
请特别留意,由于compose ui中的ConstraintLayout仍处于发展阶段,在代码编译阶段,是有一些错误没法提醒给开发者,并且运行后出现报错也很难找到对应的代码的,比如:
如果你的代码如下:
BaseTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ConstraintLayout{
val (ivMask,tv_text,tv_text2,tv_text3) = createRefs()
Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
Modifier
.constrainAs(ivMask) {
top.linkTo(parent.top, 26.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
.width(356.5.dp)
.height(49.dp))
Text(text = getString(R.string.start_device_hint),
Modifier.constrainAs(tv_text) {
top.linkTo(ivMask.top)
bottom.linkTo(ivMask.bottom)
start.linkTo(ivMask.start)
end.linkTo(ivMask.end)
}, color = Color.Blue, fontSize = 14.sp, textAlign = TextAlign.Center
)
ConstraintLayout{
Text(text = "测试内容1",
Modifier.constrainAs(tv_text2) {
top.linkTo(tv_text.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}, color = Color.Black, fontSize = 14.sp, textAlign = TextAlign.Center
)
Text(text = "哈哈哈哈",
Modifier.constrainAs(tv_text3) {
top.linkTo(tv_text2.top)
start.linkTo(tv_text2.start)
end.linkTo(tv_text2.end)
}, color = Color.Red, fontSize = 14.sp, textAlign = TextAlign.Center
)
}
}
}
}
代码编译不会报错,乍一看也没什么问题,但因为tv_text2 和 tv_text3 在内部作用域ConstraintLayout下,它们是无法访问到上层作用域的ID的,那么tv_text2设置的top.linkTo(tv_text.bottom) 会失效,就会默认从屏幕左上角开始绘制, 而设置的start.linkTo(parent.start) 和end.linkTo(parent.end) 则因为父布局ConstraintLayout 未设置宽度,则会默认使用有效宽度(等同于命令式UI的android:layout_width=“wrap_content”),如果想要文字居中,那么有两种办法,先看第一种:
BaseTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ConstraintLayout{
val (ivMask,tv_text,tv_text2,tv_text3) = createRefs()
Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
Modifier
.constrainAs(ivMask) {
top.linkTo(parent.top, 26.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
.width(356.5.dp)
.height(49.dp))
Text(text = getString(R.string.start_device_hint),
Modifier.constrainAs(tv_text) {
top.linkTo(ivMask.top)
bottom.linkTo(ivMask.bottom)
start.linkTo(ivMask.start)
end.linkTo(ivMask.end)
}, color = Color.Blue, fontSize = 14.sp, textAlign = TextAlign.Center
)
ConstraintLayout{
Text(text = getString(R.string.start_device_hint),
Modifier.constrainAs(tv_text2) {
top.linkTo(tv_text.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}.fillMaxWidth(), color = Color.Black, fontSize = 14.sp, textAlign = TextAlign.Center
//上面新增了.fillMaxWidth(),将文字的宽度全屏,但请留意,只有这个是没法让文字居中的
//是因为最早我们就写了textAlign = TextAlign.Center,所以当Text的宽度是全屏时,就居中了
)
Text(text = "测试内容",
Modifier.constrainAs(tv_text3) {
top.linkTo(tv_text2.top)
start.linkTo(tv_text2.start)
end.linkTo(tv_text2.end)
}, color = Color.Red, fontSize = 14.sp, textAlign = TextAlign.Center
)
}
}
}
}
如果上述代码中tv_text2只设置了fillMaxWidth(),而没有设置textAlign=TextAlign.Center,那么文字不会居中,但tv_text3则因为tv_text2的宽度是全屏,而设置的start.linkTo(tv_text2.start)和end.linkTo(tv_text2.end)让其文字居中
方案二:
BaseTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ConstraintLayout{
val (ivMask,tv_text,tv_text2,tv_text3) = createRefs()
Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
Modifier
.constrainAs(ivMask) {
top.linkTo(parent.top, 26.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
.width(356.5.dp)
.height(49.dp))
Text(text = getString(R.string.start_device_hint),
Modifier.constrainAs(tv_text) {
top.linkTo(ivMask.top)
bottom.linkTo(ivMask.bottom)
start.linkTo(ivMask.start)
end.linkTo(ivMask.end)
}, color = Color.Blue, fontSize = 14.sp, textAlign = TextAlign.Center
)
//让父布局的宽度全屏,那么下面的Text因为设置的start.linkTo(parent.start) 和
//end.linkTo(parent.end) 从而居中显示
ConstraintLayout(Modifier.fillMaxWidth()){
Text(text = getString(R.string.start_device_hint),
Modifier.constrainAs(tv_text2) {
top.linkTo(tv_text.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}, color = Color.Black, fontSize = 14.sp, textAlign = TextAlign.Center
)
Text(text = "测试内容",
Modifier.constrainAs(tv_text3) {
top.linkTo(tv_text2.top)
start.linkTo(tv_text2.start)
end.linkTo(tv_text2.end)
}, color = Color.Red, fontSize = 14.sp, textAlign = TextAlign.Center
)
}
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。