Jetpack Compose - ConstraintLayout
-
- 0、介紹
- 1、屬性一覽
- 2、使用示例
-
- 2.0、ID
- 2.1、Guideline
- 2.2、Barrier
- 2.3、Chain
- 2.4、小結
- 3、版本更新
- 4、未解決問題
Compose系列文章,請點原文閱讀。原文:是時候學習Compose了!
0、介紹
在xml的時代我們已經了解過了ConstraintLayout的強大功能,比例、相對位置、引導線、屏障、鍊條等讓我們開發頁面布局可以大展身手。那麼在Compose的時代,ConstraintLayout是如何使用的呢?首先官方給它的介紹很簡單:
根據子項之間的限制,定位子項的布局。
但是實際用法上有了很多的差異,接下來一起研究下吧。
1、屬性一覽
【目前基于alpha08版本的屬性】首先請看它的兩個函數:
@Composable fun ConstraintLayout(
modifier: Modifier = Modifier,
content: ConstraintLayoutScope.() -> Unit
): Unit
@Composable fun ConstraintLayout(
constraintSet: ConstraintSet,
modifier: Modifier = Modifier,
content: () -> Unit
): Unit
屬性參數含義:
參數 | 含義 |
---|---|
modifier: Modifier = Modifier | 應用于布局的修飾符 |
【用法1】content: ConstraintLayoutScope.() -> Unit | 子級内容,可以很友善的建立 限制布局參照物 |
【用法2】constraintSet: ConstraintSet, | 對于子級限制相關描述 |
【用法2】content: () -> Unit | 使用ConstraintSet參數而定義的子級内容 |
2、使用示例
我們先從一些基本概念說起:
2.0、ID
到目前為止我們并沒有見到過Compose類似xml中那種給控件命名id的形式,那既然Compose是限制布局,必須要有個控件的編号或者ID,才能實作各種限制條件, 目前在ConstraintLayout中有兩種方式來建立這種編号的方式:
如果使用的參數是ConstraintLayoutScope()的方式,那麼可以使用如下函數建立編号并應用給控件:
Modifier.constrainAs()和createRef()、createRefs()
;
如果使用的參數是ConstraintSet的方式,那麼則可以使用如下方式給控件建立ID和然後根據ID建立編号:
Modifier.layoutId()和createRefFor()
;
關于這兩種建立編号的方法我們就點到這裡,下面我們分别在Guideline和Barrier中進行示範;
2.1、Guideline
引導線,可以從特定的位置(某一方向上的偏移量或者某一方向上的比例)建立一條實際并不可見的參考線。總共有如下幾種方式:
- createGuidelineFromStart(offset: Dp)
- createGuidelineFromAbsoluteLeft(offset: Dp)
- createGuidelineFromStart(fraction: Float)
- createGuidelineFromAbsoluteLeft(fraction: Float)
- createGuidelineFromEnd(offset: Dp)
- createGuidelineFromAbsoluteRight(offset: Dp)
- createGuidelineFromEnd(fraction: Float)
- createGuidelineFromAbsoluteRight(fraction: Float)
- createGuidelineFromTop(offset: Dp)
- createGuidelineFromTop(fraction: Float)
- createGuidelineFromBottom(offset: Dp)
- createGuidelineFromBottom(fraction: Float)
别被這麼多方法吓到,其實就是從上、下、左、右四個方向,分别支援某一偏移量,或者某一比例進行建立引導線。而帶有Absolute的表示絕對的左右方向,這裡其實就是國際化的問題。
如下代碼,我們使用參數為ConstraintLayoutScope()的函數來進行示範:
@Composable
fun ConstraintLayoutDemo() {
ConstraintLayout(
modifier = Modifier.fillMaxSize()
) {
val guideline = createGuidelineFromStart(0.2f)
val (box1, box2) = createRefs()
Box(
modifier = Modifier.fillMaxSize()
.background(color = Color.Yellow)
.constrainAs(box1) {
end.linkTo(guideline)
}
)
Box(
modifier = Modifier.fillMaxSize()
.background(color = Color.Red)
.constrainAs(box2) {
start.linkTo(guideline)
}
)
}
首先ConstraintLayout是一個填充全螢幕的布局,然後在該布局中從開始(左側)的百分之20的位置,建立一條豎向引導線。
然後建立了兩個Box布局,分别是結束部分連結到引導線,開始部分連結到引導線。是以部分效果如下所示:
2.2、Barrier
屏障,這個也容易了解,如下圖所示:我們需要填寫姓名和身份證資訊,由于“姓名”和“身份證”這兩個标題長度不一緻,但是我們後面填寫的資訊又要對齊處理。你可能會想到固定前面标題的長度或者限制字元數,這樣都沒問題,但是更靈活的辦法就是使用屏障了。
我們可以在“姓名”和“身份證”這兩個标題的右邊建立一個屏障,這樣你哪個标題長,屏障就會在哪個标題的右邊,比如下圖中的紅線部分,是以後面的内容我們都可以根據屏障進行布局了。
先看下建立屏障的幾種函數:
- createStartBarrier()
- createAbsoluteLeftBarrier()
- createTopBarrier()
- createEndBarrier()
- createAbsoluteRightBarrier()
- createBottomBarrier()
一共就四個方向,加上兩個絕對方向,同樣是國際化的原因。
好了下面一起看下如何使用Barrier結合ID來實作上述的屏障效果。
首先我們在content中先聲明了3個Box布局,這三個布局都是隻指定了大小和顔色以及他們分别的ID,但是并不知道如何進行排列。
然後在constraintSet中我們根據ID建立了編号,然後對這些編号使用constrain()函數進行限制聲明,如果你在xml的時代使用ConstraintLayout布局熟悉的話,你能很快了解這裡的寫法,就是通過linkTo函數将控件的限制關系描述出來,而且目前的link文法檢查也很強大,比如說你指定一個控件的頂部需要對齊到另一個控件的左或者右部,這是不應該的,會直接報錯,也就是說
top.linkTo(parent.start)
這種類似的都是錯誤的,原因不用說了吧,代碼如下:
@Composable
fun ConstraintLayoutIdDemo() {
ConstraintLayout(
ConstraintSet {
val box1 = createRefFor("box1")
val box2 = createRefFor("box2")
val box3 = createRefFor("box3")
constrain(box1) {
top.linkTo(parent.top)
start.linkTo(parent.start)
}
constrain(box2) {
top.linkTo(box1.bottom)
start.linkTo(parent.start)
}
val barrier = createEndBarrier(box1, box2)
constrain(box3) {
start.linkTo(barrier)
top.linkTo(box1.top)
bottom.linkTo(box2.bottom)
}
}
) {
Box(
modifier = Modifier.layoutId("box1")
.background(color = Color.Red)
.width(100.dp)
.height(100.dp)
)
Box(
modifier = Modifier.layoutId("box2")
.background(color = Color.Yellow)
.width(150.dp)
.height(100.dp)
)
Box(
modifier = Modifier.layoutId("box3")
.background(color = Color.Blue)
.width(200.dp)
.height(100.dp)
)
}
}
實作結果如下所示:
修改布局參數,讓紅色部分比黃色部分寬50dp,運作結果如下:
2.3、Chain
鍊,可以參考xml中的chain,大概意思就是将一系列元件按順序打包成一行或一列。官方将此api标記為了可以改進的狀态,猜測可能會被之前提到的Flow布局代替?
先看下api,隻有兩個,建立橫向和豎向的鍊:
- createHorizontalChain()
- createVerticalChain()
第一個參數是需要打包到一起的控件的編号,第二個屬性是鍊的類型,目前共有三種類型:
-
Spread
所有控件平均分布在父布局空間中,是預設的類型
-
SpreadInside
第一個和最後一個分布在鍊條的兩端,其餘的控件平均分布剩下的空間
-
Packed
所有控件打包在一起,并放置在鍊條的中間
一起看下橫向鍊條的示例代碼,我們建立三個色彩不同的Box,然後建立橫向鍊條将這三個Box打包到一起:
@Composable
fun ConstraintLayoutChainDemo() {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val (box1, box2, box3) = createRefs()
createHorizontalChain(box1, box2, box3)
Box(modifier = Modifier.size(100.dp).background(Color.Red).constrainAs(box1) {})
Box(modifier = Modifier.size(100.dp).background(Color.Yellow).constrainAs(box2) {})
Box(modifier = Modifier.size(100.dp).background(Color.Blue).constrainAs(box3) {})
}
}
代碼渲染結果如下:
當鍊條上的元素總寬度超出螢幕時,代碼渲染結果如下,注意此時是沒有滾動效果的,請注意這種情況:
2.4、小結
其實經過上面的代碼示例,我們可以發現除了定義ID和編号外,實作限制位置的主要在constrainAs()和constrain()這兩個方法中:
我們來看下他們所支援的一些屬性:
- parent
- start
- absolutLeft
- top
- end
- absoluteRight
- bottom
-
baseline
目前有這麼多的屬性可以供我們使用,使用linkTo()連結到另一個控件的相應屬性上即可,切記一點不能亂用,你不可以讓一個控件的左側對齊另一個控件的上側,這些IDE都會直接給你報錯的。
3、版本更新
暫無
4、未解決問題
目前限制布局的基本的使用已經沒有大礙了,更多的是開發過程中的細節問題,遇到後再進行補充。在xml中的根據角度限制的功能目前在compose中還沒有發現,敬請期待吧。