关于Jetpack Compose 中 ConstraintLayout布局探索
Posted 冬天的毛毛雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于Jetpack Compose 中 ConstraintLayout布局探索相关的知识,希望对你有一定的参考价值。
ConstraintLayout
有助于依据可组合项的相对位置将它们放置到界面上,如果复杂界面需要多个Row
、Column
和Box
元素,在实现对齐要求比较复杂的较大布局时,ConstraintLayout 很有用。
注意:在 View 系统中,建议使用 [ConstraintLayout
]https://developer.android.google.cn/training/constraint-layout) 创建复杂的大型布局,因为扁平视图的层次结构性能更好。不过,这在 Compose 中不存在这个问题,与性能无关,因为它能够高效地处理较深的布局层次结构。
首先在项目的 build.gradle
文件中找到 Compose Constraint Layout 依赖项:
// build.gradle
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha07"
Compose 中的 ConstraintLayout
支持 DSL:
- 引用是使用
createRefs()
(或createRef()
)创建的,ConstraintLayout
中的每个可组合项都需要有与之关联的引用。 - 约束条件由
constrainAs
修饰符提供,该修饰符将引用作为参数,可在主体 lambda 中指定其约束条件。 - 约束条件是使用
linkTo
或其他有用的方法指定的。 parent
是一个现有的引用,可用于指定对ConstraintLayout
可组合项本身的约束条件。
1. 我们先从一个简单的例子开始xxx.linkTo(xxx)
:
@Composable
fun ConstraintLayoutDemo1()
ConstraintLayout()
val (loginBtn, userName) = createRefs()
Button(
onClick = /*TODO*/ ,
modifier = Modifier
.fillMaxWidth()
.constrainAs(loginBtn)
top.linkTo(parent.top, margin = 10.dp)
start.linkTo(parent.start, margin = 20.dp)
)
Text(text = "登录")
Text(
text = "xxx@162.com",
modifier = Modifier
.wrapContentSize()
.constrainAs(userName)
top.linkTo(loginBtn.bottom, margin = 30.dp)
// 下面两行等价于: centerHorizontallyTo(parent)
start.linkTo(parent.start)
end.linkTo(parent.end)
)
上面案例只是约束了上下左右间距
2. barrierLine约束
- 先看
centerAround()
以及createEndBarrier()
@Composable
fun ConstraintLayoutDemo2()
ConstraintLayout()
val (loginBtn, userName,password) = createRefs()
Button(
onClick = /*TODO*/ ,
modifier = Modifier
.wrapContentSize()
.constrainAs(loginBtn)
top.linkTo(userName.bottom, margin = 10.dp)
// 将该控件中心点约束在userName的结束处
centerAround(userName.end)
)
Text(text = "登录")
Text(
text = "xxx@162.com",
modifier = Modifier
.wrapContentSize()
.constrainAs(userName)
top.linkTo(parent.top, margin = 30.dp)
)
// 定义一个栅栏,在userName与loginBtn右边
val barrier = createEndBarrier(userName,loginBtn)
Text(
text = "*********",
modifier = Modifier
.wrapContentSize()
.constrainAs(password)
top.linkTo(parent.top, margin = 20.dp)
start.linkTo(barrier)
)
这里定义了一个barrierLine, 而centerAround从效果来看是将第一个控件"登录"的中心约束在了第二个控件“@162.com”结尾
- 再看看如何便捷约束到父节点中心,
centerTo
,centerHorizontallyTo
:
@Composable
fun DemoInlineDSL()
ConstraintLayout(
modifier = Modifier
.wrapContentSize()
.background(color = Color.Gray.copy(0.3f))
)
val (textA, textB, textC) = createRefs()
Text("AAA",
Modifier
.wrapContentSize()
.background(color = Color.Yellow.copy(alpha = 0.5f))
.constrainAs(textA)
start.linkTo(textB.end, margin = 10.dp)
)
Text("BBB",
Modifier
.wrapContentSize()
.background(color = Color.Green.copy(alpha = 0.4f))
.constrainAs(textB)
centerTo(parent)
)
val barrier = createBottomBarrier(textA, textB)
Text("This is a very long CCCC",
Modifier
.wrapContentSize()
.background(color = Color.Blue.copy(alpha = 0.4f))
.constrainAs(textC)
top.linkTo(barrier, margin = 10.dp)
centerHorizontallyTo(parent)
width = Dimension.preferredWrapContent.atMost(20.dp)
)
效果如下,灰色背景代表整个父布局:
- ConstraintSet 的使用,如何给控件设置id
@Composable
fun DemoConstraintSet()
ConstraintLayout(
ConstraintSet
val textA = createRefFor("id_a")
val textB = createRefFor("id_b")
val textC = createRefFor("id_c")
constrain(textA)
start.linkTo(textB.end, margin = 20.dp)
constrain(textB)
centerTo(parent)
val barrier = createBottomBarrier(textA, textB)
constrain(textC)
top.linkTo(barrier, margin = 20.dp)
centerHorizontallyTo(parent)
width = Dimension.preferredWrapContent.atMost(20.dp)
,Modifier.background(color = Color.Gray.copy(0.5f))
)
Text("Text1", Modifier.layoutId("id_a"))
Text("Text2", Modifier.layoutId("id_b"))
Text("This is a very long CCCC", Modifier.layoutId("id_c"))
效果展示:
3. guideline约束
@Composable
fun LargeConstraintLayoutDemo3()
ConstraintLayout ()
val text = createRef()
val guideline = createGuidelineFromStart(0.5f)
Text(
"什么是JSON JSON的用法 阿里云双11最高立减1111 恒创科技_海外CN2服务器26元/月起",
Modifier.constrainAs(text)
linkTo(guideline, parent.end)
,
)
垂直父节点0.5处建了一条guideLine, Text左临guideLine,右贴parent.end, 这样的话正常应该是占满guideLine右边。
实际效果是:
因为默认宽度width = Dimension.wrapContent是不允许布局大小是包裹内容大小的,所以出现了左边越界。
需要添加: width = Dimension.preferredWrapContent,即在约束条件下,指定布局大小为包裹内容的大小:
@Composable
fun LargeConstraintLayoutDemo3()
ConstraintLayout ()
val text = createRef()
val guideline = createGuidelineFromStart(0.5f)
Text(
"什么是JSON JSON的用法 阿里云双11最高立减1111 恒创科技_海外CN2服务器26元/月起",
Modifier.constrainAs(text)
linkTo(guideline, parent.end)
width = Dimension.preferredWrapContent
,
)
效果:
width的取值还有其他几种情况:
fillToConstraints - 布局将展开,以填充由该维度的约束条件定义的空间。使用
preferredValue - 布局是固定的 dp 值,受限于该维度的约束条件。
value - 布局是固定的 dp 值,无论该维度中的约束条件如何
-
Chains
链允许我们垂直或水平对齐视图,也可以控制它们之间的空间,以及视图如何使用空间。 链的创建:
createHorizontalChain(可变参数元素:ConstrainedLayoutReference,chainStyle: chainStyle = chainStyle . spread) -创建水平链。
createVerticalChain(可变参数元素:ConstrainedLayoutReference,chainStyle: chainStyle = chainStyle . spread) -创建垂直链。
- ChainStyle 有三种不同类型的连锁风格:
- Spread: 子View分布均匀。
- Spread inside: 第一个和最后一个视图与链两端的约束对齐,其余的均匀分布。
- Packed: 子View挤在一起。
看下案例:
@Composable
fun ChainExample()
ConstraintLayout(modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
.background(color = Color.Gray.copy(0.5f))
)
val (button1, button2, button3) = createRefs()
createHorizontalChain(button1, button2, button3, chainStyle = ChainStyle.Packed)
Button(onClick = , modifier = Modifier.constrainAs(button1)
)
Text(text = "Button 1")
Button(onClick = , modifier = Modifier.constrainAs(button2)
)
Text(text = "Button 2")
Button(onClick = , modifier = Modifier.constrainAs(button3)
)
Text(text = "Button 3")
以上是关于关于Jetpack Compose 中 ConstraintLayout布局探索的主要内容,如果未能解决你的问题,请参考以下文章
关于MVP的项目经验心得以及对Jetpack Compose的思考
Jetpack Compose 深入探索系列三:Compose runtime