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)者是要上位的勘高。大勢所趨峡蟋!大家都在學(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)在測試中了子漩,等到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ì)定位,圓形定位的屬性就很簡單了非区,只有如下三個(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度畫一條長度為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)嚣州。之所以是“一般不支持”鲫售,控件有在比較簡單約束條件下,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è)置滿足所有約束并維持指定長寬比的最大尺寸木蹬。
(這句話翻譯自文檔,要細(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ù)值,即控件總長度大于父控件兩邊界的間距罩引,則間隙為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í)候军援,記得使用下面這種簡便形式仅淑!不然一個(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è)比較簡單了恶耽,也是繼承于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測量后的寬高
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í)在測量完成后(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)榕澹瑴y量時(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

猜測:上文設(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)系作者
  • 序言:七十年代末白热,一起剝皮案震驚了整個(gè)濱河市敛助,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屋确,老刑警劉巖纳击,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異攻臀,居然都是意外死亡焕数,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門刨啸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來堡赔,“玉大人,你說我怎么就攤上這事呜投〖有伲” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵仑荐,是天一觀的道長雕拼。 經(jīng)常有香客問我,道長粘招,這世上最難降的妖魔是什么啥寇? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮洒扎,結(jié)果婚禮上辑甜,老公的妹妹穿的比我還像新娘。我一直安慰自己袍冷,他們只是感情好磷醋,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胡诗,像睡著了一般邓线。 火紅的嫁衣襯著肌膚如雪淌友。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天骇陈,我揣著相機(jī)與錄音震庭,去河邊找鬼。 笑死你雌,一個(gè)胖子當(dāng)著我的面吹牛器联,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播婿崭,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼拨拓,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了氓栈?” 一聲冷哼從身側(cè)響起千元,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎颤绕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祟身,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奥务,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袜硫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氯葬。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖婉陷,靈堂內(nèi)的尸體忽然破棺而出帚称,到底是詐尸還是另有隱情,我是刑警寧澤秽澳,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布闯睹,位于F島的核電站,受9級(jí)特大地震影響担神,放射性物質(zhì)發(fā)生泄漏楼吃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一妄讯、第九天 我趴在偏房一處隱蔽的房頂上張望孩锡。 院中可真熱鬧,春花似錦亥贸、人聲如沸躬窜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荣挨。三九已至男韧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間垦沉,已是汗流浹背煌抒。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厕倍,地道東北人寡壮。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像讹弯,于是被迫代替她去往敵國和親况既。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345