我之前寫過一篇 ConstraintLayout 的文章現(xiàn)在已經(jīng)到了 2018 年巷折,最新正式版本也已經(jīng)到了 1.1.2 冀墨,又加了不少好用的特性崇棠,可以說這個(gè)約束布局已經(jīng)成為 Android 中最強(qiáng)大的布局了,絕對(duì)不是吹噓妨马。
本篇文章只會(huì)講怎么使用代碼畫布局,可視化的方式精準(zhǔn)度方面還是有點(diǎn)差強(qiáng)人意而叼,如果你想了解可視化方式身笤,請(qǐng)看我之前的文章。
讓我們看一看這個(gè) Android 中最強(qiáng)大的布局吧葵陵!
相對(duì)定位
一液荸、基本用法
相對(duì)定位約束布局最基本也最常用的使用方式
我們先簡(jiǎn)單看一下用法
<TextView
android:id="@+id/a"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:gravity="center"
android:layout_marginTop="30dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:text="A"/>
<TextView
android:id="@+id/b"
app:layout_constraintTop_toTopOf="@+id/a"
app:layout_constraintLeft_toRightOf="@+id/a"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="B" />
我們先看 A 控件,A 的位置怎么來的呢脱篙?
-
app:layout_constraintTop_toTopOf="parent"
A 的頂邊與 parent 的頂邊對(duì)齊 -
app:layout_constraintLeft_toLeftOf="parent"
A 的左邊與 parent 的左邊對(duì)齊
我們看到 A 與左邊上邊都有一個(gè)空隙娇钱, 這就是普通的 android:layout_marginXXXX 屬性,我也就不細(xì)說了
我們?cè)倏?B 控件绊困,B 的位置怎么來的呢文搂?
-
app:layout_constraintTop_toTopOf="@+id/a"
B 的頂部與 A 的頂部對(duì)齊 -
app:layout_constraintLeft_toRightOf="@+id/a"
B 的左邊與 A 的右邊對(duì)齊
B 控件我沒有設(shè)置 margin 所以他們是貼在一塊的
從上面的例子我們看到了兩個(gè)問題
- 看到兩個(gè)控件
app:layout_constraintXXX_toXXXOf="xxx"
屬性的值不一樣,一個(gè)寫的是控件 ID秤朗,一個(gè)是 parent 煤蹭,這是什么意思呢?一般控件去約束需要一個(gè)參照物取视,這個(gè)參照物標(biāo)識(shí)可以是控件的 ID 硝皂,也可以是父布局(父容器) —— parent
就好比一根繩子一端拴在當(dāng)前控件的某一位置,另一端拴在參照物的某一個(gè)位置上作谭,這就建立起了約束稽物。
- 如果仔細(xì)看上述代碼,大家肯定還有一個(gè)疑問折欠,我明明在 A 控件上設(shè)置了 marginRight 為什么 A 和 B 還是貼著的贝或,這就有一個(gè)說法吼过,如果一個(gè)邊沒有約束那么他對(duì)應(yīng)邊的 margin 是不生效的
OK 理解起來很簡(jiǎn)單不是嗎,約束布局最基本的語法就是 app:layout_constraint位置_to位置Of="看齊目標(biāo)"
那么像這種普通的相對(duì)定位的寫法有多少種呢咪奖,我來給你們列舉一下盗忱,再配張圖標(biāo)出它們的具體位置,看完下面基本的約束布局用法你就已經(jīng)了解了羊赵。
layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
當(dāng)然有一部分控件沒有 Baseline 這個(gè)位置售淡,所以這個(gè)位置不是對(duì)每種控件都有效的
二、圓形定位
<View
android:id="@+id/a"
android:layout_width="120dp"
android:layout_height="50dp"
android:background="@android:color/holo_red_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/b"
android:layout_width="120dp"
android:layout_height="50dp"
android:background="@android:color/holo_blue_light"
app:layout_constraintCircle="@id/a"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="100dp" />
偷偷放一張圖片
-
app:layout_constraintCircle
需要看齊的參照物慷垮,圖中 B 就是把 A 當(dāng)做參照物進(jìn)行約束的 -
app:layout_constraintCircleAngle
要旋轉(zhuǎn)的角度揖闸,最上方 0 度,默認(rèn)就是 0 度料身,順時(shí)針開始算汤纸。 -
app:layout_constraintCircleRadius
兩個(gè)控件中心點(diǎn)的距離
約束鏈
能夠在水平或垂直方向控件之間相互約束而組成的一條鏈就是約束鏈,約束鏈?zhǔn)怯砷_頭的控件進(jìn)行屬性控制的芹血。沒錯(cuò)就是跟著大哥走
- 普通的約束鏈?zhǔn)纠?/li>
<View
android:id="@+id/a"
android:layout_width="80dp"
android:layout_height="50dp"
android:background="@android:color/holo_red_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/b"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/b"
android:layout_width="80dp"
android:layout_height="50dp"
android:background="@android:color/holo_blue_light"
app:layout_constraintLeft_toRightOf="@+id/a"
app:layout_constraintRight_toLeftOf="@+id/c"
app:layout_constraintTop_toTopOf="@+id/a" />
<View
android:id="@+id/c"
android:layout_width="80dp"
android:layout_height="50dp"
android:background="@android:color/holo_green_light"
app:layout_constraintLeft_toRightOf="@+id/b"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/a" />
看到 A 控件就是引領(lǐng)小弟們的鏈頭贮泞,所以眾小弟都聽他的。我們看到它的 app:layout_constraintHorizontal_chainStyle="packed" 屬性幔烛。
這個(gè)屬性有三種值
- packed:控件緊挨在一起啃擦。還可以通過bias屬性設(shè)置偏移量。
- spread:均與分布控件饿悬。
- spread_inside:均與分布控件令蛉,但是兩邊控件貼邊。
我貼幾張圖大概看一下樣子
當(dāng)然如果是垂直的就是 app:layout_constraintVertical_chainStyle="xxxx" 屬性
-
組合 layout_constraintHorizontal_bias 的約束鏈
如果鏈的樣式是 packed 我們還能組合 layout_constraintHorizontal_bias 使用狡恬,我就不貼代碼了珠叔,直接上圖了解下
還有一種特殊的約束鏈,就是按照控件權(quán)重平分控件(明擺著搶 LinearLayout 飯碗)
<View
android:id="@+id/a"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@android:color/holo_red_light"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/b" />
<View
android:id="@+id/b"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@android:color/holo_blue_light"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@+id/a"
app:layout_constraintRight_toLeftOf="@+id/c" />
<View
android:id="@+id/c"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@android:color/holo_green_light"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@+id/b"
app:layout_constraintRight_toRightOf="parent" />
這種鏈就是不設(shè)置鏈的 style 而是用權(quán)重的方式進(jìn)行排布弟劲。
約束布局散亂特性
一祷安、動(dòng)態(tài)推測(cè)控件的寬或高
我們約束布局還支持這樣的一種情況,我們控件只確定了控件的寬兔乞、高的其中一個(gè)汇鞭,然后按比例算出另一邊的寬高。(當(dāng)然這個(gè)需求很小眾)
app:layout_constraintDimensionRatio="2:1"
這個(gè)屬性就是按比例推測(cè)寬高的屬性
<View
android:id="@+id/a"
android:layout_width="100dp"
android:layout_height="0dp"
android:background="@android:color/holo_red_light"
app:layout_constraintDimensionRatio="2:1"
app:layout_constraintLeft_toLeftOf="parent" />
看以上代碼 2:1 其實(shí)就是 width/height = 2/1 庸追,height = 50dp霍骄,所以這個(gè)屬性的就是 width/height ,由此我們只要給定寬或高就能推出另一個(gè)锚国。(當(dāng)然如果你寬高都確定了設(shè)置這個(gè)屬性就無效了)
這個(gè)屬性的值還能設(shè)置為 (H,2:1)腕巡,(W,2:1) 這樣的玄坦,其實(shí)我看到之后是一臉懵逼的血筑,但是還要硬著頭皮研究绘沉,H 就是 Height,W 就是 Width豺总,好吧說一下規(guī)律车伞。
(H,2:1)
如果確定寬寬:高度 = 100 * 1 / 2
如果確定高度:寬度 = 100 * 1 / 2(W,2:1)
如果確定寬寬:高度 = 100 * 2 / 1
如果確定高度:寬度 = 100 * 2 / 1
雖然我這樣說了但是還是不推薦用。本來就是小眾功能如果想用還是用普通的寫法就行了喻喳,不要帶什么 H另玖,W 了。裝逼太刺眼表伦!
二谦去、父布局填充約束
有時(shí)候有這么一個(gè)需求想把控件按照比例填充父布局。Android 屏幕適配這么復(fù)雜蹦哼,好像不容易實(shí)現(xiàn)
這時(shí)候約束布局有兩個(gè)屬性 layout_constraintWidth_percent layout_constraintHeight_percent
怎么看怎么像百分比布局鳄哭,這豈不是把一直不溫不火的百分比布局給革命了
<View
android:id="@+id/a"
android:layout_width="0dp"
android:layout_height="50dp"
android:background="@android:color/holo_red_light"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintWidth_percent="0.3" />
我只放這么一段代碼大家應(yīng)該就知道怎么用了,我也就不多了說了纲熏。
默認(rèn)是居中的如果想調(diào)整位置請(qǐng)結(jié)合
app:layout_constraintHorizontal_bias="0.3"
食用
三妆丘、聊一聊 margin 屬性
有人要說了 margin 屬性還用你說?當(dāng)然了我并不會(huì)說局劲,我要說的是 layout_goneMarginStart
沒見過吧勺拣?這個(gè)是什么意思呢,如果要約束控件隱藏了鱼填,B 控件位置還想保持不動(dòng)怎么辦呢药有,那么這是個(gè)什么情況呢?
-
舉個(gè)例子 B 控件左邊相對(duì) A 控件進(jìn)行定位苹丸,并設(shè)置了 20dp 的 marginLeft塑猖。如果把 A 隱藏了會(huì)怎么樣呢?分別看一下圖1谈跛、圖2羊苟。
給我們的 B 控件加上兩個(gè)屬性
app:layout_goneMarginLeft="100dp"
app:layout_goneMarginTop="30dp"
預(yù)想的效果很棒,不是嗎感憾,從此我們看出 goneMarginXXXX 就是在約束的目標(biāo)控件隱藏時(shí)才會(huì)生效的 margin蜡励。
四、WRAP_CONTENT 的小問題
如果你的控件寬或高是 wrap_content 并且控件長(zhǎng)度過長(zhǎng)時(shí)阻桅,他的約束會(huì)失效凉倚,我們看個(gè)例子
<View
android:id="@+id/a"
android:layout_width="120dp"
android:layout_height="50dp"
android:background="@android:color/holo_red_light"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/b"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="@android:color/holo_blue_light"
android:text="是事實(shí)是事實(shí)是事實(shí)是事實(shí)是事實(shí)"
app:layout_constraintLeft_toRightOf="@+id/a"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/a" />
解決方式就是使用
app:layout_constrainedWidth="true"
app:layout_constrainedHeight="true"
五、最小高度寬度嫂沉,最大高度寬度
設(shè)置最小尺寸
layout_constraintWidth_min and layout_constraintHeight_min
設(shè)置最大尺寸
layout_constraintWidth_max and layout_constraintHeight_max
沒啥好說的稽寒。
六、控件位置偏移
app:layout_constraintHorizontal_bias="0.3" app:layout_constraintVertical_bias="0.3"
相信這兩個(gè)屬性在上面大家都看過好多遍了吧趟章,如果控件沒有占滿父布局杏糙,它是可以控制當(dāng)前控件在父布局的空間里所占的位置慎王,放張圖理解一下。就跟拔河一樣哈哈
該屬性的取值范圍是 0 - 1
這是橫向的分析宏侍,豎向與這個(gè)一致
約束布局輔助工具
一赖淤、 輔助定位線
Guideline 是一個(gè)幫助我們來定位控件,但是他又不被用戶所感知谅河。
<android.support.constraint.Guideline
android:id="@+id/line"
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="wrap_content"/>
上面的最基本的寫法
- id 是必須的不然怎么約束
- android:orientation="vertical" 來控制橫向還是豎向咱旱,用過線性布局應(yīng)該都知道這個(gè)屬性
- 他核心的三個(gè)屬性
- layout_constraintGuide_begin="100dp" 距離父容器起始位置的距離
- layout_constraintGuide_end="100dp" 距離父容器結(jié)束位置的距離
- layout_constraintGuide_percent="0.3" 距離父容器寬度或高度的百分比,取值范圍 0 - 1
如果上述三種屬性同時(shí)出現(xiàn)绷耍,優(yōu)先級(jí)由高到低 layout_constraintGuide_percent > layout_constraintGuide_begin > layout_constraintGuide_end
二吐限、 控件組
Group 可以同時(shí)控制多個(gè)控件的顯示與隱藏
<android.support.constraint.Group
android:id="@+id/group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:constraint_referenced_ids="a,b" />
- android:visibility="visible" 控制顯示隱藏,也可以在代碼中根據(jù) id 獲取 Group 來控制顯示隱藏
- app:constraint_referenced_ids="a,b" 受這個(gè) Group 管理的控件們的 id褂始,
,
號(hào)隔開
三毯盈、 控件屏障
Barrier
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="start"
app:constraint_referenced_ids="button1,button2" />
- app:barrierDirection=“start” 屬性可以控制這個(gè)屏障在哪個(gè)位置,具體位置可以參考文章開頭那張介紹位置的圖
- app:constraint_referenced_ids 屏障里面的控件們的 id病袄,
,
號(hào)隔開
下面分析一個(gè)例子
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="姓名:sdfsdfsdsdf"
app:layout_constraintBottom_toBottomOf="@+id/et_name"
app:layout_constraintTop_toTopOf="@+id/et_name"/>
<TextView
android:id="@+id/tv_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="手機(jī)號(hào):"
app:layout_constraintBottom_toBottomOf="@+id/et_phone"
app:layout_constraintTop_toTopOf="@+id/et_phone"/>
<EditText
android:id="@+id/et_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="請(qǐng)輸入姓名"
app:layout_constraintLeft_toLeftOf="@+id/barrier"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<EditText
android:id="@+id/et_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="請(qǐng)輸入手機(jī)號(hào)"
app:layout_constraintLeft_toLeftOf="@+id/barrier"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_name"/>
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="tv_name,tv_phone"/>
放一張效果圖
- 首先把 et_name et_phone 位置放好搂赋,然后再把 tv_name tv_phone 放在屏障里,把他們當(dāng)成一個(gè)整體
- 然后 et_name et_phone 左邊約束我們的屏障
- 為什么 et_name et_phone 對(duì)齊的會(huì)是屏障的右邊益缠,因?yàn)?
app:barrierDirection="right"
這個(gè)屬性控制的脑奠,如果改成 left 就會(huì)變成如下的樣子,全都和屏障的左邊去對(duì)齊了
這個(gè)屏障有點(diǎn)稍微復(fù)雜那么一丟丟幅慌,大家多多實(shí)踐一下
小結(jié)
OK 我在這里寫了約束布局的一些用法宋欺,那么下一篇我將會(huì)繼續(xù)絮叨絮叨這個(gè)約束布局在我們平常開發(fā)常用的一些寫法和技巧!
如果還有些看不懂胰伍,請(qǐng)配合官方文檔食用齿诞,畢竟官方資料才是我們的一手資料