ConstraintLayout學(xué)起來!

說點(diǎn)大家的觀點(diǎn),有點(diǎn)啰嗦

1、ConstraintLayout允許通過無嵌套視圖方式創(chuàng)建大型而復(fù)雜的布局。類似于RelativeLayout,所有視圖均根據(jù)同級(jí)視圖和父級(jí)布局之間的關(guān)系進(jìn)行布局,但是它比RelativeLayout更靈活,更易于使用。

當(dāng)然,這里有人是有不同意見的,所有控件都是同一個(gè)父View,會(huì)顯得比較散,分模塊操作時(shí)效率較低。畢竟就目前來說,也就只有Group來控制一組控件的顯示與否。
2.0之后添加的Layer會(huì)改善這種情況。

而且就正常情況下,在ConstaintLayout里面添加一些其它ViewGroup有時(shí)也是無可避免的嘛

2、Android Studio 同時(shí)還提供特有的布局編輯器,ConstraintLayout的布局內(nèi)容均可以通過拖拉拽(以及編輯器的右邊屬性欄)達(dá)成。

不過現(xiàn)在階段,慣性思維下,對(duì)于拖拉拽的不習(xí)慣以至于大多人還在觀望,即便使用上了,也會(huì)習(xí)慣性的使用編寫XML的方式!
當(dāng)然有時(shí)候只需要修改一行或幾行屬性,手寫會(huì)來得快。

哦對(duì)了,喜歡手寫的直接在編輯器的右邊屬性欄一個(gè)個(gè)添加約束,也未嘗不可。

另外2.0的基于ConstaintLayout的MotionLayout據(jù)說是特強(qiáng)大的動(dòng)畫布局,Android Studio 4.0 版本也提供了拖拉拽來實(shí)現(xiàn),到時(shí)候動(dòng)畫可能就看你的想象力了。

3、畢竟Google對(duì)于約束布局的支持是很大的,ConstaintLayout之于RelativeLayout,就像RecycleView之于ListView,終究強(qiáng)者是要上位的。大勢(shì)所趨!大家都在學(xué),你不學(xué),落后就要挨打咯。

都9102年了,別裝睡了,你還能學(xué)。

共勉。

使用方式

1. 導(dǎo)入包:
dependencies {
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    // androidx:
    // implementation "androidx.constraintlayout:constraintlayout:1.1.3"
}

目前最新版本是1.1.3。 2.0版本已經(jīng)在測(cè)試中了,等到2.0,新的特性就更多了!
什么Layer、Flow、MotionLayout等。

基礎(chǔ)使用

2. 相對(duì)定位

定義一個(gè)控件的位置,起碼要使其在縱橫方向各至少擁有一個(gè)相對(duì)約束---即相對(duì)于其它控件的位置??聪旅孢@個(gè)圖

相對(duì)約束

布局編輯器顯示控件 C 在 A下面, 但是 C并沒有設(shè)置垂直方向的約束,運(yùn)行時(shí)會(huì)默認(rèn)在父布局的頂端,與我們所預(yù)想的發(fā)生偏差。

相對(duì)約束的基本屬性格式是

layout_constraintDirection1_toDirection2Of

Direction1和Direction2可以是Left、Top、Right、Bottom其中任意的左右或者上下的組合,也可以是Start、End組合(根據(jù)從左向右布局,Start == Left,End == Right)。后續(xù)出現(xiàn)的Direction均代表這個(gè)屬性。

相對(duì)約束-方向

從屬性名我們就可直譯出其代表的意思,比如:

layout_constraintTop_toBottomOf="@id/btn1",約束該控件的上邊界在btn1的下邊界下面,且若不設(shè)置邊距(margin),則與btn1下邊界在同一水平線上。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:text="button1"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/btn1"
        app:layout_constraintLeft_toRightOf="@id/btn1"
        android:text="button2"/>

</android.support.constraint.ConstraintLayout>

那么例子中btn2的相對(duì)約束就是在btn1的右下角。(btn1中的parent表示相對(duì)父布局的位置)

相對(duì)約束-例子

當(dāng)然還有一種常用的文字基線對(duì)齊,屬于垂直方向的約束,與RelativeLayout的alignBaseLine屬性相似

layout_constraintBaseline_toBaselineOf
相對(duì)約束-基線
3. Margin

用于設(shè)置與其它控件的邊距。
與其它Layout類型不同的是,Margin的設(shè)置依賴于控件是否有添加相應(yīng)方向的相對(duì)約束。

當(dāng)設(shè)置了某個(gè)方向的邊界的相對(duì)約束之后,該方向設(shè)置的margin才能生效!
否則margin無效。

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginRight="20dp"
        android:text="button1"/>

上述android:layout_marginRight="20dp"無效。當(dāng)然如果你通過布局編輯器操作的話,本身就無法寫入這一條無效語句,如果你用的是xml寫入的這一條,然后再去編輯器編輯該控件的時(shí)候,會(huì)發(fā)現(xiàn)這條語句也會(huì)被優(yōu)化而刪除!

特殊情況:設(shè)置了Margin的控件的visibility屬性變?yōu)閂iew.Gone
  • View.Gone 的控件在約束布局中,依然可以通過findViewById()找到,只是寬高都為0dp,即視為一個(gè)點(diǎn),且其每個(gè)方向的margin也都變?yōu)?
View.Gone
  • 由于View A 已經(jīng)Gone,則其他依賴于View A的,如View B的位置會(huì)有相應(yīng)的變化,防止出現(xiàn)顯示異常,View B通過可以設(shè)置layout_goneMarginDireaction來設(shè)置當(dāng)View A Gone時(shí)候的間距。如
      app:layout_goneMarginLeft="20dp"

4.圓形定位

相較于相對(duì)定位,圓形定位的屬性就很簡(jiǎn)單了,只有如下三個(gè)約束

<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
  app:layout_constraintCircle="@+id/buttonA"
  app:layout_constraintCircleRadius="100dp"
  app:layout_constraintCircleAngle="45" />

解讀下就是:以ButtonA的中心點(diǎn)作為原點(diǎn),從原點(diǎn)處以Y軸正半軸向右偏離45度畫一條長(zhǎng)度為100dp的線段,線段的另一個(gè)頂點(diǎn)為ButtonB的中心點(diǎn)!

圓形定位

值得注意的是,圓形定位優(yōu)先于相對(duì)定位。

Android Studio 當(dāng)前版本(3.5)并沒有直接支持拖拽來寫這些角度。。不寫相對(duì)約束居然還飄紅,有點(diǎn)過分

5. 居中與傾向(Biaz)

這個(gè)就有點(diǎn)意思

<!--水平方向添加左右兩條約束-->
<TextView
    android:id="@+id/tv1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="TextView"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toEndOf="@+id/btn1"
    app:layout_constraintEnd_toStartOf="@+id/btn2"
/>

當(dāng)控件在水平(或垂直)方向左右(上下)同時(shí)使用了相對(duì)約束,那么控件會(huì)位于兩個(gè)約束控件的正中間。

比如說上述代碼,tv1的約束條件是,在btn1的右邊,btn2的左邊,按照規(guī)定,三個(gè)控件在水平線上應(yīng)該是緊緊相鄰的,但是這里不可能做到,除非tv1的寬度剛好等于btn1與btn2的間距。

所以在這種約束規(guī)則下,tv1的表現(xiàn)為位于btn1和btn2的正中間。

居中

當(dāng)然有時(shí)候需要的不僅是居中而是中間偏左,或者偏上之類的。
那么要需要設(shè)置:

//居中默認(rèn)為 0.5,取值0.0-1.0
 //小于0.5即偏左(也不一定,就比如上述例子,若btn2與btn1間距小于tv的寬度
 //那么小于0.5就偏右了)
app:layout_constraintHorizontal_bias = "0.5"  

//垂直方向同理
app:layout_constraintVertical_bias = "0.5"

另外1,在此規(guī)則下,若將相對(duì)應(yīng)的寬高設(shè)置為0dp,則控件會(huì)撐滿間距!同時(shí)bias設(shè)置無效。

另外2,上述這個(gè)例子中,根據(jù)tv1的約束,若btn2在btn1的左邊會(huì)發(fā)生什么呢?

實(shí)際上tv1的中心點(diǎn)依然會(huì)在這btn1和btn2的兩條約束邊界的中間,此時(shí)設(shè)置bias小于0.5時(shí)tv1會(huì)偏右。

6.寬高比

作為ConstraintLayout的子控件,其寬高一般是不支持設(shè)置為match_parent的,而是使用match_constraint代替(xml中使用0dp表示match_constraint)。之所以是“一般不支持”,控件有在比較簡(jiǎn)單約束條件下,match_parent是和0dp等效的,所以還是用0dp就可以了。

使用match_constraint的控件最好同時(shí)有設(shè)置其左右/上下的約束組合!否則,有可能會(huì)真的是0dp。

進(jìn)入正題,當(dāng)有寬高至少有一邊設(shè)置為0dp時(shí),我們可以設(shè)置該控件的寬高比!

  1. 當(dāng)只有一邊設(shè)置為0dp:
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintDimensionRatio="1:2"
// 默認(rèn)格式為,寬:高,可通過添加W或H來改變,如 "H,1:2" 為高:寬 = 2:1
此時(shí)"1:2" == "W,1:2" == "H,2:1", W/H 用于指定分子與分母

該控件寬高比會(huì)變?yōu)?:2,由于layout_width="0dp",則寬度隨著高度變化而變化。

  1. 如若寬高都為0dp。

在這種情況下,系統(tǒng)將設(shè)置滿足所有約束并維持指定長(zhǎng)寬比的最大尺寸。
(這句話翻譯自文檔,要細(xì)品。)

假設(shè)控件A在水平的左右方向都存在約束,垂直方向只有一條約束。相對(duì)于垂直方向,A在水平方向上的寬度比較固定(等于屏幕寬度),所以高度會(huì)根據(jù)比例跟著變化。

假設(shè)控件A在水平方向以及垂直方向都有存在約束,那就可以通添加w或h來指定約束方向。

    app:layout_constraintDimensionRatio="w,1:2"  //或  "h,1:2"
    "w,1:2"表示 寬度根據(jù)高度變化而變化,且寬高比依舊是1:2
    "h,1:2"表示 高度根據(jù)寬度變化而變化,且寬高比依舊是1:2
    W/H 是用于指定約束方向

7.尺寸約束

定義layout_width和layout_height的時(shí)候,同樣是有三種方式:固定值、wrap_content、0dp

  1. 使用 wrap_content的時(shí)候,可以使用如下來限制控件大小
    android:minWidth 設(shè)置布局的最小寬度
    android:minHeight 設(shè)置布局的最小高度
    android:maxWidth 設(shè)置布局的最大寬度
    android:maxHeight 設(shè)置布局的最大高度
  1. 使用0dp時(shí)則可以使用:
    layout_constraintWidth_min、layout_constraintHeight_min:將為此控件設(shè)置最小尺寸
    layout_constraintWidth_max、layout_constraintHeight_max:將為此控件設(shè)置最大尺寸
    layout_constraintWidth_percent、layout_constraintHeight_percent:將此控件的尺寸設(shè)置為父控件的百分比
輔助工具類

終于到了重中之重了,這些拓展的輔助工具類才是ConstaintLayout真香于RelativeLayout的地方。

8. Chain

鏈雖然沒有一個(gè)具體的類,比如Chain.java,但是也算一種特殊的約束,就也歸入輔助工具類吧。在2.0版本將見到更強(qiáng)大的Flow輔助類。

鏈,兩個(gè)及以上的控件兩兩相互約束。且頭尾兩邊的控件受約束于同一水平軸的其他非此鏈成員控件(比如parent),鏈才能正常生效。約束效果如下圖

通過鏈頭(最靠左邊或上邊的控件)設(shè)置如下屬性來達(dá)到不同分布效果

//layout_constraintHorizontal_chainStyle
layout_constraintVertical_chainStyle = "spread_inside|spread|packed"
鏈類型

下述便于解說,就圖上的例子,鏈兩邊控件(A和C)的約束控件為父控件parent

  1. Spread

默認(rèn)的類型,在充分考慮了margin之后,鏈上的控件均勻分布(在考慮margin之后的,布局剩余的空間,均勻分配給在各個(gè)控件的間隙,包括與parent的間隙。若剩余空間為負(fù)值,即控件總長(zhǎng)度大于父控件兩邊界的間距,則間隙為0,此時(shí)鏈居中,兩邊超出屏幕外的控件自生自滅)

  1. Spread inside

A和C控件固定在鏈的兩端的約束上,即貼著parent,其余控件均勻分布。

相對(duì)于Spread布局,不同的是,布局剩余的空間不考慮兩邊控件與parent的間隙。當(dāng)然若是空隙為負(fù),表現(xiàn)則同Spread模式

  1. Weighted

加權(quán)分布,在上述這兩種模式中,若有一控件將寬度設(shè)置為0dp,那么該控件將充滿剩余的空間。而且,類似于LinearLayout,對(duì)于剩余的空間可以通過設(shè)置每個(gè)控件的權(quán)重屬性:

app:layout_constraintHorizontal_weight = 1
//app:layout_constraintVertical_weight = 1 

根據(jù)權(quán)重為不同的控件分配不同比例的空間。

  1. Packed

將每個(gè)控件緊貼(需要考慮margin)在一起,剩余的空間間隙分配在兩邊控件與parent之間。而且可以通過調(diào)節(jié)鏈頭的bias來分配兩邊間隙。
若間隙小于0,表現(xiàn)如同Spread。

另外生成鏈的時(shí)候,記得使用下面這種簡(jiǎn)便形式!不然一個(gè)一個(gè)控件去添加約束,累死個(gè)人了。


chain.gif

9. Guideline

指導(dǎo)線,作為其它控件的約束準(zhǔn)則。其它控件可以方便的通過GuideLine進(jìn)行定位。

GuideLine繼承于View,但并不會(huì)在布局中呈現(xiàn)(View.Gone)

可以通過設(shè)置下面三種屬性之一來設(shè)置GuideLine的位置

app:layout_constraintGuide_begin="100dp" //與parent左邊界或上邊界(根據(jù)GuideLine的方向)的距離
app:layout_constraintGuide_end="100dp" ////與parent右邊界或下邊界(根據(jù)GuideLine的方向)的距離
app:layout_constraintGuide_percent="0.5"  // 百分比, 0~1


android:orientation="vertical|horizontal" //設(shè)置方向
GuideLine

10. Barrier

柵欄,類似于GuideLine,設(shè)置為View.GONE,也是設(shè)置輔助線的作用,不過這個(gè)輔助線取決于多個(gè)控件的同一側(cè)邊界。當(dāng)所依賴的控件大小有所變化的時(shí)候,Barrier也有可能跟著變化。

假設(shè)Barrier定義如下:

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/barrier"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:barrierDirection="right"  // 或left、top、bottom
        app:constraint_referenced_ids="buttonA,buttonB" //引用多個(gè)控價(jià),用逗號(hào)隔開
        />
        
    <Button
        android:id="@+id/buttonC"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintStart_toEndOf="@+id/barrier"
        app:layout_constraintTop_toTopOf="parent"
        />

即Barrier的位置取決于buttonA和buttonB誰的右邊界更靠右,而ButtonC在Barrier的右邊。

Barrier

這里還有個(gè)知識(shí)點(diǎn):Barrier 繼承于 ConstraintHelper,而ConstraintHelper繼承于View.

ConstraintHelper是用于管理一組控件的行為,與ViewGroup不同的是:1.不增加層級(jí);2. 不同的Helper可以引用同一個(gè)控件

在2.0版本支持自定義Helper。

11. Group

這個(gè)比較簡(jiǎn)單了,也是繼承于ConstraintHelper, 用于控制一組控件的顯示與否。

<androidx.constraintlayout.widget.Group
              android:id="@+id/group"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:visibility="visible"
              app:constraint_referenced_ids="button1,button2" />

值得注意的是,由于控件被包含在Group中,通過 View.setVisibility(int)來控制控件的顯示與否,是無效的。

另外,當(dāng)一個(gè)控件被添加在不同的Group中,此時(shí)這根據(jù)布局文件中排最后一個(gè)的Group將具有一票否決權(quán)。

12. PlaceHolder

占位是指提前設(shè)置一個(gè)繪制內(nèi)容為空的控件,根據(jù)約束完成定位后,在恰當(dāng)?shù)臅r(shí)候?qū)⑦@個(gè)PlaceHolder的位置提供給其它控件使用!

設(shè)置占位并綁定指定的控件的方式有兩種:

<android.support.constraint.Placeholder
    android:id="@+id/pl"
    android:layout_width="50dp"
    android:layout_height="50dp"
    app:content="@id/btn1"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

或者

placeHolder.setContentId(R.id.btn1)

當(dāng)一個(gè)控件A被綁定至PlaceHolder,有如下反應(yīng)。

  1. A在原位置上會(huì)被當(dāng)做View.Gone. 其它依賴于A的控件會(huì)把A當(dāng)做一個(gè)點(diǎn)來處理。
  2. PlaceHolder的其它約束條件不變,寬高變成了A的寬高
  3. 在PlaceHolder的位置顯示出A的內(nèi)容

雖然目前這個(gè)功能對(duì)我來說很雞肋,但對(duì)這個(gè)功能實(shí)現(xiàn)感興趣,所以我覺得這個(gè)可以稍微了解更深點(diǎn)的

在源碼中我們看到這幾處代碼:

PlaceHolder.class
//根據(jù)綁定的控件,更新PlaceHolder測(cè)量后的寬高
public void updatePostMeasure(ConstraintLayout container) {
    if (this.mContent != null) {
        LayoutParams layoutParams = (LayoutParams)this.getLayoutParams();
        LayoutParams layoutParamsContent = (LayoutParams)this.mContent.getLayoutParams();
        layoutParamsContent.widget.setVisibility(0);
        // 這里
        layoutParams.widget.setWidth(layoutParamsContent.widget.getWidth());
        layoutParams.widget.setHeight(layoutParamsContent.widget.getHeight());
        layoutParamsContent.widget.setVisibility(8);  //控件不可見
    }
}

//PlaceHolder.class
// 在layout()之前將綁定的控件 layoutParamsContent.isInPlaceholder = true
public void updatePreLayout(ConstraintLayout container) {
    if (this.mContentId == -1 && !this.isInEditMode()) {
        this.setVisibility(this.mEmptyVisibility);
    }

    this.mContent = container.findViewById(this.mContentId);
    if (this.mContent != null) {
        LayoutParams layoutParamsContent = (LayoutParams)this.mContent.getLayoutParams();
        layoutParamsContent.isInPlaceholder = true; //這個(gè)屬性
        this.mContent.setVisibility(0);
        this.setVisibility(0);
    }

}


ConstraintLayout.class
// 更新綁定的控件的位置
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    int widgetsCount = this.getChildCount();
    boolean isInEditMode = this.isInEditMode();

    int helperCount;
    for(helperCount = 0; helperCount < widgetsCount; ++helperCount) {
        View child = this.getChildAt(helperCount);
        ...
            if (child instanceof Placeholder) {
                Placeholder holder = (Placeholder)child;
                View content = holder.getContent();
                if (content != null) {
                    content.setVisibility(0);
                    content.layout(l, t, r, b); //更改所綁定控件的顯示位置
                }
            }
        }
    }
    ...
}

用文字來描述就是,當(dāng)一個(gè)控件A被添加到PlaceHolder后,會(huì)被標(biāo)記為isInPlaceholder=true,且A被設(shè)置為View.Gone, 那么其它依賴于A的控件就會(huì)把A當(dāng)成一個(gè)點(diǎn)。

同時(shí)在測(cè)量完成后(layout之前),將PlaceHolder的寬高修改為A的寬高

接著在onLayout過程時(shí),A設(shè)為View.VISIABLE, PlaceHolder的布局位置讓給A,即執(zhí)行A.layout(l,t,r,b)(參數(shù)來自PlaceHolder)

此時(shí)雖然A可見了,但依賴于A的其它控件的大小及位置已經(jīng)與A無關(guān)了。(這里沒深究,大小是因?yàn)?,測(cè)量時(shí)因?yàn)锳為Gone,布局位置應(yīng)當(dāng)是與A.layoutParams.isInPlaceholder == true相關(guān))

Optimizer

優(yōu)化器,系統(tǒng)會(huì)自動(dòng)嘗試減少視圖的約束,從而提高布局速度

官方還沒啥使用文檔。。

給出了這么個(gè)使用方式:

添加:app:layout_optimizationLevel 到 ConstraintLayout的標(biāo)簽中

app:layout_optimizationLevel="direct|barrier|chain"

這個(gè)屬性值有六種,standard為默認(rèn)形式,即會(huì)優(yōu)化direct和barrier這兩種類型的約束

none : no optimizations are applied  // 不優(yōu)化
standard : Default. Optimize direct and barrier constraints only
direct : optimize direct constraints
barrier : optimize barrier constraints
chain : optimize chain constraints (experimental) // 實(shí)驗(yàn)性
dimensions : optimize dimensions measures (experimental), reducing the number of measures of match constraints elements // 實(shí)驗(yàn)性

關(guān)于這個(gè)幾個(gè)屬性,在這個(gè)問答中有比較詳細(xì)的解釋。大家自己看看吧。
https://stackoverflow.com/questions/49802490/what-is-constraintlayout-optimizer

猜測(cè):上文設(shè)置無效margin約束,會(huì)自動(dòng)被優(yōu)化刪除,可能就是這個(gè)觸發(fā)的?

ConstrainsSet 與 ConstaintLayout.LayoutParams

ConstaintLayout.LayoutParams 顧名思義,就是我們布局參數(shù)了。

通常我們可以通過修改布局參數(shù)值來控制一個(gè)控件的呈現(xiàn)形式。

但是ConstaintLayout的特殊性,如果要做一些比較復(fù)雜的變更步驟就會(huì)變得繁瑣,比如說在Chain中加入一個(gè)控件,你覺得還行?那將幾個(gè)控件組成一條鏈呢?

所以谷歌的建議是,使用ConstrainsSet來進(jìn)行動(dòng)態(tài)修改控件的參數(shù)。ConstrainsSet.createHorizontalChain(...) 就可以創(chuàng)建一條鏈,不過這里更多是需要理解方法中的參數(shù)

接著了解下使用方式:

  1. 生成ConstaintSet對(duì)象。
無參對(duì)象
val c = new ConstraintSet(); 

從已存在的layout中導(dǎo)出所有子控件的約束形成約束集
c.clone(context, R.layout.layout1);
c.clone(cLayout);
  1. 修改指定的控件的約束條件
    c.setAlpha(int viewId, float alpha)
    c.constrainHeight(int viewId, int height)
    
    //設(shè)置控件間的相對(duì)約束,side的取值為1~7 
    //即:ConstaintSet.LEFT、ConstaintSet.RIGHT... 等上述相對(duì)布局可使用的7個(gè)Dreaction
    c.connect(int startID, int startSide, int endID, int endSide)
    ...
    
    //基本可通過xml設(shè)置的屬性,在ConstraintSet中都能找相對(duì)應(yīng)的方法
  1. 使步驟2中的修改生效
c.applyTo(cLayout);

// cLayout的所有控件必須都設(shè)置有viewId,因?yàn)樵摲椒ㄓ腥缦屡袛啵?
    if (id == -1) {
         throw new RuntimeException("All children of ConstraintLayout must have ids to use ConstraintSet");
    }

// 不過我注意到2.0版本是這樣的,可以通過setForceId(boolean b) 來控制是否都需要設(shè)置id
    if (this.mForceId && id == -1) {
         throw new RuntimeException("All children of ConstraintLayout must have ids to use ConstraintSet");
    }


這里有個(gè)關(guān)鍵地方需要提下:

  • 若在第一步ConstraintSet使用的是無參數(shù)的構(gòu)造方法
val set = ConstraintSet()
set.setMargin(R.id.btn1, ConstraintSet.LEFT, 300)
set.constrainWidth(R.id.btn1, 300)
set.applyTo(cLayout)

那么在setMargin()時(shí)會(huì)生成一個(gè)Constraints對(duì)象用來存R.id.btn1的這條Margin約束。(ConstraintSet使用Map關(guān)聯(lián)viewId和Constraints)

在appleTo(cLayout)的時(shí)候上述的Constraints替換cLayout中的R.id.btn1原本定義在xml的所有約束條件!

另外還有個(gè)可能就是,cLayout不存在id為R.id.btn1的子控件。那一般就什么都不會(huì)發(fā)生。

從源碼來看,對(duì)于這個(gè)cLayout中不存在的id,若我們使用了這樣的代碼。。

val set = ConstraintSet()
set.createBarrier(R.id.btn1, ...)
set.applyTo(cLayout)

那么btn1會(huì)被作為一個(gè)新的Barrier控件加入cLayout中(addView()的方式),同理的還有GuideLine。

本文最重要的點(diǎn)

  1. 實(shí)踐出真章。多實(shí)戰(zhàn),你會(huì)發(fā)現(xiàn)還挺好用的,然后發(fā)現(xiàn)文中一些錯(cuò)誤的主觀觀點(diǎn)...
  1. 參考自官方文檔:

https://developer.android.com/reference/android/support/constraint/ConstraintLayout

https://developer.android.com/training/constraint-layout

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容