天天看點

Jetpack Compose - ConstraintLayout

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布局,分别是結束部分連結到引導線,開始部分連結到引導線。是以部分效果如下所示:

Jetpack Compose - ConstraintLayout

2.2、Barrier

屏障,這個也容易了解,如下圖所示:我們需要填寫姓名和身份證資訊,由于“姓名”和“身份證”這兩個标題長度不一緻,但是我們後面填寫的資訊又要對齊處理。你可能會想到固定前面标題的長度或者限制字元數,這樣都沒問題,但是更靈活的辦法就是使用屏障了。

我們可以在“姓名”和“身份證”這兩個标題的右邊建立一個屏障,這樣你哪個标題長,屏障就會在哪個标題的右邊,比如下圖中的紅線部分,是以後面的内容我們都可以根據屏障進行布局了。

Jetpack Compose - ConstraintLayout

先看下建立屏障的幾種函數:

  • 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)
        )
    }
}
           

實作結果如下所示:

Jetpack Compose - ConstraintLayout

修改布局參數,讓紅色部分比黃色部分寬50dp,運作結果如下:

Jetpack Compose - ConstraintLayout

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) {})
    }
}
           

代碼渲染結果如下:

Jetpack Compose - ConstraintLayout

當鍊條上的元素總寬度超出螢幕時,代碼渲染結果如下,注意此時是沒有滾動效果的,請注意這種情況:

Jetpack Compose - ConstraintLayout

2.4、小結

其實經過上面的代碼示例,我們可以發現除了定義ID和編号外,實作限制位置的主要在constrainAs()和constrain()這兩個方法中:

我們來看下他們所支援的一些屬性:

  • parent
  • start
  • absolutLeft
  • top
  • end
  • absoluteRight
  • bottom
  • baseline

    目前有這麼多的屬性可以供我們使用,使用linkTo()連結到另一個控件的相應屬性上即可,切記一點不能亂用,你不可以讓一個控件的左側對齊另一個控件的上側,這些IDE都會直接給你報錯的。

3、版本更新

暫無

4、未解決問題

目前限制布局的基本的使用已經沒有大礙了,更多的是開發過程中的細節問題,遇到後再進行補充。在xml中的根據角度限制的功能目前在compose中還沒有發現,敬請期待吧。

繼續閱讀