再谈ConstraintLayout
ConstraintLayout 是我们Android开发中常见的以"锚点"为布局的容器,某位UI大湿曾经有句名言:我能用ConstraintLayout实现所有布局!的确,ConstraintLayout特有的layout_constraintxx定义,其实更加符合UI开发的直觉,描述约束本身的成本其实是很少的,同时后期可维护性非常强。因为后续想要添加其他子内容的时候,我们只需要处理好约束本身即可。因此,在AndroidStudio的默认工程中,新建一个xml默认都是以ConstraintLayout为根布局,这也看出来官方对其的重视程度。
不仅如此,ConstraintLayout还被Android团队运用在声明式UI框架Compose当中,成为最重要的组件之一
css
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout {
// Create references for the composables to constrain
val (button, text) = createRefs()
Button(
onClick = { /* Do something */ },
// Assign reference "button" to the Button composable
// and constrain it to the top of the ConstraintLayout
modifier = Modifier.constrainAs(button) {
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text("Button")
}
// Assign reference "text" to the Text composable
// and constrain it to the bottom of the Button composable
Text("Text", Modifier.constrainAs(text) {
top.linkTo(button.bottom, margin = 16.dp)
})
}
}
同时Android团队把ConstraintLayout作为基础,扩展到了更加复杂的MotionLayout中,作为简化开发动画的利器。
RelativeContainer
既然描述约束这么好用,那么Harmony OS中,有没有类似的组件呢?当然有,毕竟谁也不想让开发们遇上多嵌套的Row跟Column(太难受了)
RelativeContainer就是在鸿蒙开发者,承担着解决多重嵌套的布局利器。
默认情况下,RelativeContainer跟普通组件没有什么区别
scss
RelativeContainer() {
Text("text1")
.fontSize(25)
.width(200)
.id("text1") // 必须添加 id,否则不显示
.textAlign(TextAlign.Center)
.backgroundColor("#ccaabb")
}
.width('100%')
.height(200)
.backgroundColor("#111111")
子View text1会默认处于RelativeContainer的左上角
我们可以通过alignRules 方法,声明需要的约束,下面我们来详细认识一下这些锚点
锚点
alignRules 接受一个AlignRuleOption 实现,里面提供了几个具体的锚点
php
declare interface AlignRuleOption {
/**
* The param of left align.
* @form
* @since 9
*/
left?: { anchor: string, align: HorizontalAlign };
/**
* The param of right align.
* @form
* @since 9
*/
right?: { anchor: string, align: HorizontalAlign };
/**
* The param of middle align.
* @form
* @since 9
*/
middle?: { anchor: string, align: HorizontalAlign };
/**
* The param of top align.
* @form
* @since 9
*/
top?: { anchor: string, align: VerticalAlign };
/**
* The param of bottom align.
* @form
* @since 9
*/
bottom?: { anchor: string, align: VerticalAlign };
/**
* The param of center align.
* @form
* @since 9
*/
center?: { anchor: string, align: VerticalAlign };
}
锚点 举例
我们拿right 举例子
less
Text("text1")
.fontSize(25)
.width(200)
.id("text1") // 必须添加 id,否则不显示
.textAlign(TextAlign.Center)
.backgroundColor("#ccaabb")
.alignRules({
right: {
anchor: "__container__",
align: HorizontalAlign.End
},
})
这里alignRules 可以填入锚点方向
css
right?: { anchor: string, align: HorizontalAlign };
right 代表着当前组件需要定位的方向,anchor代表着锚点,以anchor为参考系,align表示对齐方式
这里针对anchor有一个特殊值,"__container__
" 代表着以父布局为参考系。
right: { anchor: "container", align: HorizontalAlign.End }, 这个含义是指,当前子组件Text的right方向以父布局为基准,然后按照右侧对齐
针对水平方向align为HorizontalAlign
- HorizontalAlign.Start: 设置子组件右边框相对锚点组件的左边框位置对齐。
- HorizontalAlign.Center: 设置子组件右边框相对锚点组件的中间点位置对齐。
- HorizontalAlign.End: 设置子组件右边框相对锚点组件的右边框位置对齐。
当然,单个left跟right对齐其实跟大部分的声明式UI框架没有太多区别,这里我特别举出一个例子,比如我们想要实现一个组件居中对齐,习惯了ConstraintLayout,我们可能会直接对left跟right进行对齐,比如错误示例
css
right: {
anchor: "__container__",
align: HorizontalAlign.End
},
left: {
anchor: "__container__",
align: HorizontalAlign.Start
},
这样的话,其实只会把子布局width撑大到父布局,比如
这里也说明了,当多个方向的锚点被设置,其实效果是满足锚点,同时会把子组件自身的width属性的数据丢失(本来设置的width为200)
想要实现水平居中,RelativeContainer 采取middle对齐的方式实现,
css
middle:{
anchor: "__container__",
align: HorizontalAlign.Center
},
垂直居中的话,就要采取center对齐
css
center:{
anchor: "__container__",
align: VerticalAlign.Center
},
其他的几个,欢迎读者们自行尝试一下效果。
除了以父组件为参考系以外,我们还可以以其他子组件为参考系,比如我们放置两个子Text
less
RelativeContainer() {
Text("text1")
.fontSize(25)
.id("text1") // 必须添加 id,否则不显示
.textAlign(TextAlign.Center)
.backgroundColor("#ccaabb")
.alignRules({
left:{
anchor:"__container__",
align: HorizontalAlign.Start
},
right:{
anchor:"__container__",
align: HorizontalAlign.End
},
})
Text("text2")
.fontSize(25)
.id("text2") // 必须添加 id,否则不显示
.textAlign(TextAlign.Center)
.backgroundColor("#bbccaa")
.alignRules({
left:{
anchor:"__container__",
align: HorizontalAlign.Start
},
right:{
anchor:"__container__",
align: HorizontalAlign.End
},
// 以text1 为参考系,顶部对齐
top:{
anchor:"text1",
align:VerticalAlign.Bottom
}
})
}
.width('100%')
.height(200)
.backgroundColor("#111111")
对应的效果图如下
注意点
这里总结几个开发过程中会遇到的几个需要注意的点
width等自身属性会被锚点覆盖
这里我们上面例子也给出来了,当我们设置组件自身固有属性,比如width的时候,如果设置了相同方向的多个锚点,比如同时设置left 跟right,那么组件的宽度会以锚点的布局为最终宽度
子组件需要设置id
如果子组件没有设置id,那么组件会不被绘制展示,比如我们把text2的id拿掉后
less
没有id
Text("text2")
.fontSize(25)
.textAlign(TextAlign.Center)
.backgroundColor("#bbccaa")
.alignRules({
left:{
anchor:"__container__",
align: HorizontalAlign.Start
},
right:{
anchor:"__container__",
align: HorizontalAlign.End
},
// 以text1 为参考系,顶部对齐
top:{
anchor:"text1",
align:VerticalAlign.Bottom
}
})
效果如下,text2将不被绘制
避免align 错误影响
align参数也会影响到布局展示,比如
scss
RelativeContainer() {
Text("text1")
.fontSize(25)
.id("text1")
.textAlign(TextAlign.Center)
.backgroundColor("#ccaabb")
.alignRules({
left:{
anchor:"__container__",
align: HorizontalAlign.End
},
})
}
.width('100%')
.height(200)
.backgroundColor("#111111")
}
.width('100%')
如果left 对齐的位置是父view的end,那么其实就是没有展示空间从而不被展示
总结
本章中我们学习到了(鸿蒙版本ConstraintLayout) RelativeContainer 的用法,同时官方的资料中其实对RelativeContainer没有太多补充,因此我在这里提一下,当然,RelativeContainer在每个api版本中某些行为还是不太一致的,比如环形依赖的问题,这里大家可以实际操作体验一下。