Android ConstraintLayout完全解析和效能分析(章節二)
這篇文章是基於Android ConstraintLayout完全解析和效能分析(章節一)基礎上對屬性的深入詳解,如果之前對Android ConstraintLayout不瞭解或者不會使用的,請檢視章節一的內容。若是有一定的瞭解,想深入對ConstraintLayout屬性的瞭解及效能的分析,請直接閱讀本章節內容。
一、ConstraintLayout屬性講解
先簡單瞭解一下我們使用ConstraintLayout要用到的一些基本方位屬性,如下表所示:
屬性 | 描述 |
---|---|
app:layout_constraintLeft_toLeftOf | 把A的left side放在B的left side(左邊對齊) |
app:layout_constraintLeft_toRightOf | 把A的left side放在B的right side(左邊相對右邊對齊) |
app:layout_constraintRight_toLeftOf | 把A的right side放在B的left side(右邊相對左邊對齊) |
app:layout_constraintRight_toRightOf | 把A的right side放在B的right side(右邊對齊) |
app:layout_constraintTop_toTopOf | 把A的top side放在B的top side(頂部對齊) |
app:layout_constraintTop_toBottomOf | 把A的top side放在B的bottom side(頂部相對底部對齊) |
app:layout_constraintBottom_toTopOf | 把A的bottom side放在B的top side(底部相對頂部對齊) |
app:layout_constraintBottom_toBottomOf | 把A的bottom side放在B的bottom side(底部對齊) |
app:layout_constraintStart_toEndOf | 把A的start position放在B的end position(起始位置相對結束位置對齊) |
app:layout_constraintStart_toStartOf | 把A的start position放在B的start position(起始位置對齊) |
app:layout_constraintEnd_toStartOf | 把A的end position放在B的start position(結束位置相對起始位置對齊) |
app:layout_constraintEnd_toEndOf | 把A的end position放在B的end position(結束位置對齊) |
app:layout_constraintBaseline_toBaselineOf | 把A的bottom side放在B的top side(基準線對齊) |
注意:屬性的名稱空間是app
一看這麼多屬性,估計整個人都不健康了,那讓我們先看一個簡單的例子吧:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
通過以上的這段佈局,既可以完成下圖所展示的佈局。
讓一個控制元件在螢幕居中是不是特別簡單,那讓我們在看一個不同方位的控制元件展示,程式碼如下:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</android.support.constraint.ConstraintLayout>
展示效果如下圖:
有沒有感腳這些屬性特別像RelativeLayout中的android:layout_alignParentXXX,沒錯,是特別像,但是作用會有略微不同。
上面講述了讓控制元件相對於螢幕位置展示的效果,那如果想根據控制元件和控制元件之間的相對位置進行擺放的話,那麼可以參考如下的使用方式進行處理:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.317"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.444" />
<Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="52dp"
android:layout_marginRight="52dp"
android:text="B"
app:layout_constraintBottom_toBottomOf="@+id/button2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/button2"
app:layout_constraintVertical_bias="0.0" />
</android.support.constraint.ConstraintLayout>
佈局的內碼表很簡單,就是把A控制元件作為參照位置,然後再根據A的位置來定位B控制元件的相對位置。
那麼通過上面的例子,你會發現,RelativeLayout的相對位置的操作,ConstraintLayout也能輕易實現。需要說明的是:這些屬性的值既可以是parent,也可以是某個View的id
二、偏斜(Bias)
在使用LinearLayout的時候,我們通常會使用Gravity來將水平或者垂直排列的控制元件按照權重的分配進行排列。而在ConstraintLayout中,它提供了bias屬性來對控制元件進行權重的分配。
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="A"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.25"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
以上程式碼我們可以簡單的看出,當一個控制元件被約束在螢幕的左側,又被約束在螢幕的右側,結果這個控制元件顯示在了中間的位置,為什麼? (1) 當使用了bias屬性並設定了0.25的偏移量之後,我們發現控制元件在水平方向上向左偏移了螢幕寬度的1/4距離 (2) 如果我們把偏移值改成0.75,我們可以看到控制元件在水平方向上向右偏移了1/4的距離。
效果圖如下:
那麼有的同學可能就會有疑問了,既然橫向可以設定螢幕的佔比,那縱向是不是也有相應的屬性呢,恭喜你答對了,是有的。
屬性 | 描述 |
---|---|
app:layout_constraintHorizontal_bias | 水平方向偏移係數 |
app:layout_constraintVertical_bias | 垂直方向偏移係數 |
需要注意的是,它的取值區間為:0~1
三、GONE不是真的GONE
我們在使用RelativeLayout開發時,經常會碰到這麼一種邏輯,當某個資料為空的時候,該資料所繫結的控制元件則GONE掉。那麼這樣會出現這樣的問題,例如有三個控制元件A、B、C垂直排列,B在A的下方,C在B的下方,那B隱藏(GONE)掉了之後,由於C對於A並沒有依賴關係,所以會導致頁面錯誤。這個問題ConstraintLayout給出了很好的解決方案。當在ConstraintLayout中,若一個控制元件隱藏(GONE)之後,它會變成一個點,因此對於把該控制元件作為參考控制元件的其它控制元件依然具有約束作用。
當這個控制元件隱藏之後,我們還可以參照這個隱藏的控制元件對其他控制元件設定邊距的屬性。
屬性 | 描述 |
---|---|
app:layout_goneMarginLeft | 隱藏控制元件左邊距 |
app:layout_goneMarginRight | 隱藏控制元件右邊距 |
app:layout_goneMarginTop | 隱藏控制元件頂部邊距 |
app:layout_goneMarginBottom | 隱藏控制元件底部邊距 |
app:layout_goneMarginStart | 隱藏控制元件起始邊距 |
app:layout_goneMarginEnd | 隱藏控制元件結束邊距 |
四、寬高比
1、View的寬高比
在ConstraintLayout中,還可以將寬定義成高的一個比例或者高定義成寬的比例。首先,需要先將寬或者高設定為0dp(即MATCH_CONSTRAINT),既要適應約束條件。然後通過layout_constraintDimensionRatio屬性設定一個比例即可。這個比例可以是“浮點數”,表示寬度和高度之間的比例;也可以是“寬度:高度”形式的比例。比如:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="-------------------寬高比2:1-------------------"
app:layout_constraintDimensionRatio="2:1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>
這裡將按鈕的高度設定為寬度的一半了。如下圖所示:
如果寬和高都設定為0dp(即MATCH_CONSTRAINT),那麼layout_constraintDimensionRatio的值需要先加一個"W,寬度:高度"或者"H,寬度:高度"來表示約束寬度或者高度,程式碼如下:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Button1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,10:6"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
效果圖如下:
這例子是說,首先寬度將滿足父佈局的約束,然後將安裝10:6的比例設定高度。
2、View百分比寬高
ConstraintLayout還能使用百分比來設定view的寬高,要使用百分比寬度或高度需要設定成0dp(即MATCH_CONSTRAINT)。
然後設定如下屬性即可:
app:layout_constraintWidth_default="percent" //設定寬為百分比
app:layout_constraintWidth_percent="0.3" //0到1之間的值
或
app:layout_constraintHeight_default="percent" //設定高為百分比
app:layout_constraintHeight_percent="0.3" //0到1之間的值
其中layout_constraintHeight_default或者layout_constraintWidth_default的屬性有三種類型:percent、spread、wrap,預設的為percent。
例子程式碼如下:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Button1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintWidth_percent="0.3"
app:layout_constraintWidth_default="percent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
效果圖如下:
五、複雜佈局的福星-Chain屬性
Chain鏈時一種特殊的約束讓多個chain鏈連結的Views能夠平分剩餘空間位置,在Android傳統佈局特徵裡面最相似的應該是LinearLayout中的權重比weight,但Chains鏈能做到的遠遠不止權重比weight的功能。如果要實現一個控制元件之間的鏈結構,我們可以將這些控制元件之間建立起互相的約束關係,並在這些關係的基礎上選擇建立水平方向的鏈還是垂直方向的鏈。
官方提供了一共有5種樣式的鏈:
我們可以通過下面的方式設定:
app:layout_constraintHorizontal_chainStyle="spread|spread_inside|packed"
或者
app:layout_constraintVertical_chainStyle="spread|spread_inside|packed"
在這裡需要注意的是:在Chain的第一個元件上設定chainStyle,切記 切記
1、屬性引數和值
對於Chian屬性的種類來說,一共兩種:水平方向(Horizontal)和垂直方向(Vertical)
屬性 | 描述 |
---|---|
app:layout_constraintHorizontal_chainStyle | 水平方向上的Chain |
app:layout_constraintVertical_chainStyle | 垂直方向上的Chain |
對於Chain屬性的模式來說,一共三種:展開(spread)、內部展開(spread_inside)、包裹(packed)
2. Spread鏈模式
spread模式會會把空間平均分配開來,每個View佔有各自的平分空間,它是Chain屬性的預設模式。
在該模式下,在設定View的寬度和高度為非0dp時,展示效果如下:
當使用了這個模式的時候,我們還可以配合weight屬性設定spread的權重,在設定權重的時候,我們需要將控制元件的width或者height設定成0dp,並設定layout_constraintHorizontal_weight或者layout_constraintVertical_weight的值:
屬性 | 描述 |
---|---|
app:layout_constraintVertical_weight | 垂直方向的控制元件權重 |
app:layout_constraintHorizontal_weight | 水平方向的控制元件權重 |
通過權重的設定,如圖:
3. Spread Inside鏈模式
spread inside模式是在Spread的基礎上,把兩邊最邊緣的兩個View到外向父元件邊緣的距離去除,然後讓剩餘的Views在剩餘的空間內部平分空間。
在該模式下,在設定View的寬度和高度為非0dp時,展示效果如下:
4、Packed鏈模式
packed模式很直觀,它將所有Views聚攏在一起,控制元件和控制元件之間不留間隙,並將聚攏之後的Views居中顯示。
在該模式下,在設定View的寬度和高度為非0dp時,展示效果如下:
當我們使用了Packed模式了之後,我們還可以通過bias引數對聚攏在一起的Views進行位置的調整。
5、XML中設定Chain示例
設定Chain屬性需要保證兩個條件:
- 定義chain鏈的約束條件
- 在Chain的第一個元件上設定chainStyle。
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintEnd_toStartOf="@+id/button17"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="39dp" />
<Button
android:id="@+id/button17"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintEnd_toStartOf="@+id/button18"
app:layout_constraintStart_toEndOf="@+id/button16"
tools:layout_editor_absoluteY="39dp" />
<Button
android:id="@+id/button18"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/button17"
tools:layout_editor_absoluteY="39dp" />
</android.support.constraint.ConstraintLayout>