我之前寫過一篇 ConstraintLayout 的文章現在已經到了 2018 年,最新正式版本也已經到了 1.1.2 ,又加了不少好用的特性,可以說這個限制布局已經成為 Android 中最強大的布局了,絕對不是吹噓。
本篇文章隻會講怎麼使用代碼畫布局,可視化的方式精準度方面還是有點差強人意,如果你想了解可視化方式,請看我之前的文章。
讓我們看一看這個 Android 中最強大的布局吧!
相對定位
一、基本用法
相對定位限制布局最基本也最常用的使用方式
我們先簡單看一下用法
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"/>
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 的頂邊對齊
app:layout_constraintLeft_toLeftOf="parent" A 的左邊與 parent 的左邊對齊
我們看到 A 與左邊上邊都有一個空隙, 這就是普通的 android:layout_marginXXXX 屬性,我也就不細說了
我們再看 B 控件,B 的位置怎麼來的呢?
app:layout_constraintTop_toTopOf="@+id/a" B 的頂部與 A 的頂部對齊
app:layout_constraintLeft_toRightOf="@+id/a" B 的左邊與 A 的右邊對齊
B 控件我沒有設定 margin 是以他們是貼在一塊的
從上面的例子我們看到了兩個問題
看到兩個控件 app:layout_constraintXXX_toXXXOf="xxx" 屬性的值不一樣,一個寫的是控件 ID,一個是 parent ,這是什麼意思呢?一般控件去限制需要一個參照物,這個參照物辨別可以是控件的 ID ,也可以是父布局(父容器) —— parent
就好比一根繩子一端拴在目前控件的某一位置,另一端拴在參照物的某一個位置上,這就建立起了限制。
如果仔細看上述代碼,大家肯定還有一個疑問,我明明在 A 控件上設定了 marginRight 為什麼 A 和 B 還是貼着的,這就有一個說法,如果一個邊沒有限制那麼他對應邊的 margin 是不生效的
OK 了解起來很簡單不是嗎,限制布局最基本的文法就是 app:layout_constraint位置_to位置Of="看齊目标" 那麼像這種普通的相對定位的寫法有多少種呢,我來給你們列舉一下,再配張圖示出它們的具體位置,看完下面基本的限制布局用法你就已經了解了。
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
複制代碼當然有一部分控件沒有 Baseline 這個位置,是以這個位置不是對每種控件都有效的
二、圓形定位
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" />
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 當做參照物進行限制的
app:layout_constraintCircleAngle 要旋轉的角度,最上方 0 度,預設就是 0 度,順時針開始算。
app:layout_constraintCircleRadius 兩個控件中心點的距離
限制鍊
能夠在水準或垂直方向控件之間互相限制而組成的一條鍊就是限制鍊,限制鍊是由開頭的控件進行屬性控制的。沒錯就是跟着大哥走
普通的限制鍊示例
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" />
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" />
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 控件就是引領小弟們的鍊頭,是以衆小弟都聽他的。我們看到它的 app:layout_constraintHorizontal_chainStyle="packed" 屬性。
這個屬性有三種值
packed:控件緊挨在一起。還可以通過bias屬性設定偏移量。
spread:均與分布控件。
spread_inside:均與分布控件,但是兩邊控件貼邊。
我貼幾張圖大概看一下樣子
當然如果是垂直的就是 app:layout_constraintVertical_chainStyle="xxxx" 屬性
組合 layout_constraintHorizontal_bias 的限制鍊
如果鍊的樣式是 packed 我們還能組合 layout_constraintHorizontal_bias 使用,我就不貼代碼了,直接上圖了解下
還有一種特殊的限制鍊,就是按照控件權重平分控件(明擺着搶 LinearLayout 飯碗)
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" />
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" />
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" />
複制代碼
這種鍊就是不設定鍊的 style 而是用權重的方式進行排布。
限制布局散亂特性
一、動态推測控件的寬或高
我們限制布局還支援這樣的一種情況,我們控件隻确定了控件的寬、高的其中一個,然後按比例算出另一邊的寬高。(當然這個需求很小衆)
app:layout_constraintDimensionRatio="2:1" 這個屬性就是按比例推測寬高的屬性
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 其實就是 width/height = 2/1 ,height = 50dp,是以這個屬性的就是 width/height ,由此我們隻要給定寬或高就能推出另一個。(當然如果你寬高都确定了設定這個屬性就無效了)
這個屬性的值還能設定為 (H,2:1),(W,2:1) 這樣的,其實我看到之後是一臉懵逼的,但是還要硬着頭皮研究,H 就是 Height,W 就是 Width,好吧說一下規律。
(H,2:1)
如果确定寬寬:高度 = 100 * 1 / 2
如果确定高度:寬度 = 100 * 1 / 2
(W,2:1)
如果确定寬寬:高度 = 100 * 2 / 1
如果确定高度:寬度 = 100 * 2 / 1
雖然我這樣說了但是還是不推薦用。本來就是小衆功能如果想用還是用普通的寫法就行了,不要帶什麼 H,W 了。裝逼太刺眼!
二、父布局填充限制
有時候有這麼一個需求想把控件按照比例填充父布局。Android 螢幕适配這麼複雜,好像不容易實作
這時候限制布局有兩個屬性 layout_constraintWidth_percent layout_constraintHeight_percent
怎麼看怎麼像百分比布局,這豈不是把一直不溫不火的百分比布局給革命了
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" />
複制代碼
我隻放這麼一段代碼大家應該就知道怎麼用了,我也就不多了說了。
預設是居中的如果想調整位置請結合 app:layout_constraintHorizontal_bias="0.3" 食用
三、聊一聊 margin 屬性
有人要說了 margin 屬性還用你說?當然了我并不會說,我要說的是 layout_goneMarginStart 沒見過吧?這個是什麼意思呢,如果要限制控件隐藏了,B 控件位置還想保持不動怎麼辦呢,那麼這是個什麼情況呢?
舉個例子 B 控件左邊相對 A 控件進行定位,并設定了 20dp 的 marginLeft。如果把 A 隐藏了會怎麼樣呢?分别看一下圖1、圖2。
給我們的 B 控件加上兩個屬性
app:layout_goneMarginLeft="100dp"
app:layout_goneMarginTop="30dp"
複制代碼
預想的效果很棒,不是嗎,從此我們看出 goneMarginXXXX 就是在限制的目标控件隐藏時才會生效的 margin。
四、WRAP_CONTENT 的小問題
如果你的控件寬或高是 wrap_content 并且控件長度過長時,他的限制會失效,我們看個例子
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" />
android:id="@+id/b"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:background="@android:color/holo_blue_light"
android:text="是事實是事實是事實是事實是事實"
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"
複制代碼
五、最小高度寬度,最大高度寬度
設定最小尺寸
layout_constraintWidth_min and layout_constraintHeight_min
設定最大尺寸
layout_constraintWidth_max and layout_constraintHeight_max
沒啥好說的。
六、控件位置偏移
app:layout_constraintHorizontal_bias="0.3" app:layout_constraintVertical_bias="0.3"
相信這兩個屬性在上面大家都看過好多遍了吧,如果控件沒有占滿父布局,它是可以控制目前控件在父布局的空間裡所占的位置,放張圖了解一下。就跟拔河一樣哈哈
該屬性的取值範圍是 0 - 1
這是橫向的分析,豎向與這個一緻
限制布局輔助工具
一、 輔助定位線
Guideline 是一個幫助我們來定位控件,但是他又不被使用者所感覺。
android:id="@+id/line"
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="wrap_content"/>
複制代碼
上面的最基本的寫法
id 是必須的不然怎麼限制
android:orientation="vertical" 來控制橫向還是豎向,用過線性布局應該都知道這個屬性
他核心的三個屬性
layout_constraintGuide_begin="100dp" 距離父容器起始位置的距離
layout_constraintGuide_end="100dp" 距離父容器結束位置的距離
layout_constraintGuide_percent="0.3" 距離父容器寬度或高度的百分比,取值範圍 0 - 1
如果上述三種屬性同時出現,優先級由高到低 layout_constraintGuide_percent > layout_constraintGuide_begin > layout_constraintGuide_end
二、 控件組
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" 控制顯示隐藏,也可以在代碼中根據 id 擷取 Group 來控制顯示隐藏
app:constraint_referenced_ids="a,b" 受這個 Group 管理的控件們的 id,, 号隔開
三、 控件屏障
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” 屬性可以控制這個屏障在哪個位置,具體位置可以參考文章開頭那張介紹位置的圖
app:constraint_referenced_ids 屏障裡面的控件們的 id,, 号隔開
下面分析一個例子
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"/>
android:id="@+id/tv_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="手機号:"
app:layout_constraintBottom_toBottomOf="@+id/et_phone"
app:layout_constraintTop_toTopOf="@+id/et_phone"/>
android:id="@+id/et_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="請輸入姓名"
app:layout_constraintLeft_toLeftOf="@+id/barrier"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
android:id="@+id/et_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="請輸入手機号"
app:layout_constraintLeft_toLeftOf="@+id/barrier"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_name"/>
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 放在屏障裡,把他們當成一個整體
然後 et_name et_phone 左邊限制我們的屏障
為什麼 et_name et_phone 對齊的會是屏障的右邊,因為 app:barrierDirection="right" 這個屬性控制的,如果改成 left 就會變成如下的樣子,全都和屏障的左邊去對齊了
這個屏障有點稍微複雜那麼一丢丢,大家多多實踐一下
小結
OK 我在這裡寫了限制布局的一些用法,那麼下一篇我将會繼續絮叨絮叨這個限制布局在我們平常開發常用的一些寫法和技巧!
如果還有些看不懂,請配合官方文檔食用,畢竟官方資料才是我們的一手資料
參考資料