再學一次ConstraintLayout 一些新特性

平時使用ConstraintLayout,斷斷續續的,基本都是在本身的小demo裏面使用.公司的項目暫時尚未使用.此次公司項目須要大改,我決定用上這個nice的佈局.減小嵌套(以前的老代碼,實在是嵌套得太深了....無力吐槽).javascript

首先,ConstraintLayout是一個新的佈局,它是直接繼承自ViewGroup的,因此在兼容性方面是很是好的.官方稱能夠兼容到API 9.能夠放心食用.css

1、Relative positioning

先來看看下面一段簡單示例:java

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按鈕1"/> <Button android:id="@+id/btn2" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintLeft_toRightOf="@+id/btn1" android:text="按鈕2"/> </android.support.constraint.ConstraintLayout> 

上面有一個簡單的屬性:layout_constraintLeft_toRightOf,表示將按鈕2放到按鈕1的左邊.若是沒有這一句屬性,那麼兩個按鈕會重疊在一塊兒,就像FrameLayout.android

像這樣的屬性還有不少:canvas

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

上面的屬性都很是好理解,除了一個相對陌生的layout_constraintBaseline_toBaselineOf基線對齊.我們上代碼:ruby

<TextView
        android:id="@+id/btn1" android:text="按鈕1" android:textSize="26sp"/> <TextView android:id="@+id/btn2" android:text="按鈕2" app:layout_constraintBaseline_toBaselineOf="@+id/btn1" app:layout_constraintLeft_toRightOf="@+id/btn1"/> 

一目瞭然,至關於文字的基線是對齊了的.若是沒有加layout_constraintBaseline_toBaselineOf屬性,那麼是下面這樣的:bash

 

2、與父親邊緣對齊

當須要子view放在父view的底部或者最右側時. 咱們使用:架構

<android.support.constraint.ConstraintLayout app:layout_constraintEnd_toEndOf="parent"> <TextView android:text="按鈕2" app:layout_constraintBottom_toBottomOf="parent"/> </android.support.constraint.ConstraintLayout> 
app:layout_constraintBottom_toBottomOf="parent" 個人底部與父親底部對齊 app:layout_constraintTop_toTopOf="parent" 個人頂部與父親的頂部對齊 app:layout_constraintLeft_toLeftOf="parent" 個人左側與父親的左側對齊 app:layout_constraintRight_toRightOf="parent" 個人右側與父親的右側對齊 

3、居中對齊

下面的TextView,與父親左側對齊,與父親右側對齊,因此,最右,它水平居中對齊.app

<TextView app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> 

可能你也想到了,居中對齊其實就是2個對齊方式相結合.最後產生的效果. 好比:ide

這是垂直居中
app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" 
位於父親的正中央
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" 

4、邊距

邊距和原來是同樣的.

android:layout_marginStart android:layout_marginEnd android:layout_marginLeft android:layout_marginTop android:layout_marginRight android:layout_marginBottom 

舉個例子:

<TextView
    android:id="@+id/btn1" android:text="按鈕1" android:textSize="26sp"/> <TextView android:id="@+id/btn2" android:text="按鈕2" android:layout_marginStart="40dp" app:layout_constraintLeft_toRightOf="@+id/btn1"/> 

效果以下:

 

Bias(偏向某一邊)

上面的水平居中,是使用的與父親左側對齊+與父親右側對齊. 能夠理解爲左右的有一種約束力,默認狀況下,左右的力度是同樣大的,那麼view就居中了.

當左側的力度大一些時,view就會偏向左側.就像下面這樣.

當咱們須要改變這種約束力的時候,須要用到以下屬性:

layout_constraintHorizontal_bias  水平約束力
layout_constraintVertical_bias  垂直約束力

來舉個例子:

<android.support.constraint.ConstraintLayout
    <Button
        android:text="按鈕1" app:layout_constraintHorizontal_bias="0.3" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> </android.support.constraint.ConstraintLayout> 

能夠看到,左右有2根約束線.左側短一些.那麼就偏向於左側

5、Circular positioning (Added in 1.1)

翻譯爲:圓形的定位 ?

這個就比較牛逼了,能夠以角度和距離約束某個view中心相對於另外一個view的中心,

可能比較抽象,來看看谷歌畫的圖:

他的屬性有:

layout_constraintCircle :引用另外一個小部件ID
layout_constraintCircleRadius :到其餘小部件中心的距離
layout_constraintCircleAngle :小部件應該處於哪一個角度(以度爲單位,從0到360)

舉個例子:

<Button android:id="@+id/btn1" android:text="按鈕1"/> <Button android:text="按鈕2" app:layout_constraintCircle="@+id/btn1" app:layout_constraintCircleRadius="100dp" app:layout_constraintCircleAngle="145"/> 

6、Visibility behavior 可見性行爲

當一個View在ConstraintLayout中被設置爲gone,那麼你能夠把它當作一個點(這個view全部的margin都將失效). 這個點是假設是實際存在的.

舉個例子:

<Button
    android:id="@+id/btn1" android:text="按鈕1" android:textSize="26sp"/> <Button android:id="@+id/btn2" android:layout_marginStart="20dp" android:text="按鈕2" android:visibility="gone" app:layout_constraintLeft_toRightOf="@+id/btn1"/> <Button android:id="@+id/btn3" android:layout_marginStart="20dp" android:text="按鈕3" app:layout_constraintLeft_toRightOf="@+id/btn2"/> 

能夠看到,按鈕3和按鈕1中間的margin只有20.

再舉個例子:

<Button
    android:id="@+id/btn2" android:layout_marginStart="20dp" android:text="按鈕2" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/> <Button android:id="@+id/btn3" android:text="按鈕3" app:layout_constraintLeft_toRightOf="@+id/btn2" app:layout_constraintTop_toTopOf="@+id/btn2" app:layout_constraintBottom_toBottomOf="@+id/btn2"/> 

我將按鈕3放到按鈕2的右側,這時是沒有給按鈕2加android:visibility="gone"的.

如今咱們來給按鈕2加上android:visibility="gone"

這時,按鈕2至關於縮小成一個點,那麼按鈕3仍是在他的右側不離不棄.

7、Dimensions constraints 尺寸限制

在ConstraintLayout中,能夠給一個view設置最小和最大尺寸.

屬性以下(這些屬性只有在給出的寬度或高度爲wrap_content時纔會生效):

android:minWidth 設置佈局的最小寬度 android:minHeight 設置佈局的最小高度 android:maxWidth 設置佈局的最大寬度 android:maxHeight 設置佈局的最大高度 

8、Widgets dimension constraints 寬高約束

平時咱們使用android:layout_width和 android:layout_height來指定view的寬和高.

在ConstraintLayout中也是同樣,只不過多了一個0dp.

  • 使用長度,例如
  • 使用wrap_content,view計算本身的大小
  • 使用0dp,至關於「 MATCH_CONSTRAINT」

下面是例子

<Button
    android:id="@+id/btn1" android:layout_width="100dp" android:layout_height="wrap_content" android:text="按鈕1" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"/> <Button android:id="@+id/btn2" android:layout_width="0dp" android:layout_height="wrap_content" android:text="按鈕2" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn1"/> <Button android:id="@+id/btn3" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="60dp" android:text="按鈕3" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn2"/> 

展現出來的是:

9、WRAP_CONTENT:強制約束(在1.1中添加)

當一個view的寬或高,設置成wrap_content時,若是裏面的內容實在特別寬的時候,他的約束會出現問題.咱們來看一個小栗子:

<Button
    android:id="@+id/btn1" android:layout_width="100dp" android:layout_height="wrap_content" android:text="Q"/> <Button android:id="@+id/btn2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV" app:layout_constraintLeft_toRightOf="@id/btn1" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@id/btn1"/> 

從右側的圖片能夠看出,按鈕2裏面的內容確實是在按鈕1的內容的右側.可是按鈕2整個來講,倒是沒有整個的在按鈕1的右側.

這時須要用到下面2個屬性

app:layout_constrainedWidth=」true|false」 app:layout_constrainedHeight=」true|false」 

給按鈕2加一個app:layout_constrainedWidth="true"來看效果:

哈哈,又看到了咱們想要的效果.爽歪歪.

10、MATCH_CONSTRAINT尺寸(在1.1中添加)

當一個view的長寬設置爲MATCH_CONSTRAINT(即0dp)時,默認是使該view佔用全部的可用的空間. 這裏有幾個額外的屬性

layout_constraintWidth_min和layout_constraintHeight_min:將設置此維度的最小大小
layout_constraintWidth_max和layout_constraintHeight_max:將設置此維度的最大大小
layout_constraintWidth_percent和layout_constraintHeight_percent:將此維度的大小設置爲父級的百分比

這裏簡單舉個百分比的例子:居中而且view的寬是父親的一半

<Button
    android:id="@+id/btn1" android:layout_width="0dp" android:layout_height="wrap_content" android:text="Q" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintWidth_percent="0.5"/> 

It's so easy! 這極大的減小了咱們的工做量.

注意

  • 百分比佈局是必須和MATCH_CONSTRAINT(0dp)一塊兒使用
  • layout_constraintWidth_percent 或layout_constraintHeight_percent屬性設置爲0到1之間的值

11、按比例設置寬高(Ratio)

能夠設置View的寬高比例,須要將至少一個約束維度設置爲0dp(即MATCH_CONSTRAINT),再設置layout_constraintDimensionRatio

舉例子:

<Button android:layout_width="0dp" android:layout_height="0dp" android:text="按鈕" app:layout_constraintDimensionRatio="16:9" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent"/> 

該比率可表示爲:

  • 浮點值,表示寬度和高度之間的比率
  • 「寬度:高度」形式的比率

若是兩個尺寸都設置爲MATCH_CONSTRAINT(0dp),也可使用比率。在這種狀況下,系統設置知足全部約束的最大尺寸並保持指定的縱橫比。要根據另外一個特定邊的尺寸限制一個特定邊,能夠預先附加W,「或」 H,分別約束寬度或高度。例如,若是一個尺寸受兩個目標約束(例如,寬度爲0dp且以父節點爲中心),則能夠指示應該約束哪一邊,經過 在比率前添加字母W(用於約束寬度)或H(用於約束高度),用逗號分隔:

<Button android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintDimensionRatio="H,16:9" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent"/> 

上面的代碼將按照16:9的比例設置按鈕的高度,而按鈕的寬度將匹配父項的約束。

12、Chains(鏈)

設置屬性layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle鏈的第一個元素時,鏈的行爲將根據指定的樣式(默認值CHAIN_SPREAD)更改。

  • CHAIN_SPREAD - 元素將展開(默認樣式)
  • 加權連接CHAIN_SPREAD模式,若是設置了一些小部件MATCH_CONSTRAINT,它們將分割可用空間
  • CHAIN_SPREAD_INSIDE - 相似,但鏈的端點不會分散
  • CHAIN_PACKED - 鏈條的元素將被包裝在一塊兒。而後,子項的水平或垂直誤差屬性將影響打包元素的定位


下面是一個相似LinearLayout的weight的效果,須要用到layout_constraintHorizontal_weight屬性:

<Button
    android:id="@+id/btn1" android:layout_width="0dp" android:layout_height="wrap_content" android:text="A" app:layout_constraintEnd_toStartOf="@id/btn2" app:layout_constraintHorizontal_chainStyle="spread" app:layout_constraintHorizontal_weight="1" app:layout_constraintStart_toStartOf="parent"/> <Button android:id="@+id/btn2" android:layout_width="0dp" android:layout_height="wrap_content" android:text="按鈕2" app:layout_constraintEnd_toStartOf="@id/btn3" app:layout_constraintHorizontal_weight="2" app:layout_constraintStart_toEndOf="@id/btn1"/> <Button android:id="@+id/btn3" android:layout_width="0dp" android:layout_height="wrap_content" android:text="問問" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_weight="3" app:layout_constraintStart_toEndOf="@id/btn2"/> 

例子的效果圖以下:

十3、Guideline

這是一個虛擬視圖

Guideline能夠建立相對於ConstraintLayout的水平或者垂直準線. 這根輔助線,有時候能夠幫助咱們定位.

layout_constraintGuide_begin   距離父親起始位置的距離(左側或頂部)
layout_constraintGuide_end    距離父親結束位置的距離(右側或底部)
layout_constraintGuide_percent    距離父親寬度或高度的百分比(取值範圍0-1)

咱們拿輔助線幹嗎??? 好比有時候,可能會有這樣的需求,有兩個按鈕,在屏幕中央一左一右. 若是是之前的話,我會搞一個LinearLayout,.而後將LinearLayout居中,而後按鈕一左一右.

效果圖以下:

如今咱們使用Guideline的話,就超級方便了,看代碼:

<!--水平居中--> <android.support.constraint.Guideline android:id="@+id/gl_center" android:layout_width="0dp" android:layout_height="wrap_content" android:orientation="vertical" app:layout_constraintGuide_percent="0.5"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按鈕1" app:layout_constraintEnd_toStartOf="@id/gl_center"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按鈕2" app:layout_constraintLeft_toRightOf="@id/gl_center"/> 

十4、Barrier

虛擬視圖

Barrier是一個相似於屏障的東西.它和Guideline比起來更加靈活.它能夠用來約束多個view.

好比下面的姓名和聯繫方式,右側的EditText是確定須要左側對齊的,左側的2個TextView能夠當作一個總體,Barrier會在最寬的那個TextView的右邊,而後右側的EditText在Barrier的右側.

Barrier有2個屬性

  • barrierDirection,取值有top、bottom、left、right、start、end,用於控制 Barrier 相對於給定的 View 的位置。好比在上面的栗子中,Barrier 應該在 姓名TextView 的右側,所以這裏取值right(也可end,可隨意使用.這個right和end的問題,其實在RelativeLayout中就有體現,在RelativeLayout中寫left或者right時會給你一個警告,讓你換成start和end)。

  • constraint_referenced_ids,取值是要依賴的控件的id(不須要@+id/)。Barrier 將會使用ids中最大的一個的寬(高)做爲本身的位置。

ps:這個東西有一個小坑,若是你寫完代碼,發現沒什麼問題,可是預覽出來的效果卻不是你想要的.這時,運行一下程序便可.而後預覽就正常了,在手機上展現的也是正常的.

例子的代碼以下(若是預覽不正確,那麼必定要運行一下,不要懷疑是本身代碼寫錯了):

<TextView
    android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="姓名:" app:layout_constraintBottom_toBottomOf="@id/tvTitleText"/> <TextView android:id="@+id/tv_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="聯繫方式:" app:layout_constraintBottom_toBottomOf="@id/tvContentText" app:layout_constraintTop_toBottomOf="@+id/tv_name"/> <EditText android:id="@+id/tvTitleText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="null" android:text="張三" android:textSize="14sp" app:layout_constraintStart_toEndOf="@+id/barrier2"/> <EditText android:id="@+id/tvContentText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="null" android:text="xxxxxxxxxxxxxxx" android:textSize="14sp" app:layout_constraintStart_toEndOf="@+id/barrier2" app:layout_constraintTop_toBottomOf="@+id/tvTitleText"/> <android.support.constraint.Barrier android:id="@+id/barrier2" android:layout_width="wrap_content" android:layout_height="wrap_content" app:barrierDirection="right" app:constraint_referenced_ids="tv_name,tv_phone"/> 

十5、Group

固定思議,這是一個組. 這也是一個虛擬視圖.

能夠把View放到裏面,而後Group能夠同時控制這些view的隱藏.

<android.support.constraint.Group android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" app:constraint_referenced_ids="btn1,btn2"/> <Button android:id="@+id/btn1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按鈕1"/> <Button android:id="@+id/btn2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="按鈕2" app:layout_constraintTop_toBottomOf="@id/btn1"/> 
  • Group有一個屬性constraint_referenced_ids,能夠將那些須要同時隱藏的view丟進去.

  • 別將view放Group包起來.這樣會報錯,由於Group只是一個不執行onDraw()的View.

  • 使用多個 Group 時,儘可能不要將某個View重複的放在 多個 Group 中,實測可能會致使隱藏失效.

十6、何爲虛擬視圖

上面咱們列舉的虛擬視圖一共有:

  • Guideline
  • Barrier
  • Group

來咱們看看源碼

//Guideline public class Guideline extends View { public Guideline(Context context) { super(context); //這個8是什麼呢? //public static final int GONE = 0x00000008; //實際上是View.GONE的值 super.setVisibility(8); } public Guideline(Context context, AttributeSet attrs) { super(context, attrs); super.setVisibility(8); } public Guideline(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); super.setVisibility(8); } public Guideline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr); super.setVisibility(8); } //可見性永遠爲GONE public void setVisibility(int visibility) { } //沒有繪畫 public void draw(Canvas canvas) { } //大小永遠爲0 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { this.setMeasuredDimension(0, 0); } 

咱們看到Guideline實際上是一個普通的View,而後在構造函數裏將本身設置爲GONE

  • 而且setVisibility()爲空方法,該View就永遠爲GONE了.
  • draw()方法爲空,意思是不用去繪畫.
  • onMeasure()中將本身長寬設置成0.

綜上所述,我以爲這個Guideline就是一個不可見的且不用測量,不用繪製,那麼咱們就能夠忽略其繪製消耗.

而後Barrier和Group都是繼承自ConstraintHelper的,ConstraintHelper是一個View.ConstraintHelper的onDraw()和onMeasure()以下:

public void onDraw(Canvas canvas) { } protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //mUseViewMeasure一直是false,在Group中用到了,可是仍是將它置爲false了. if (this.mUseViewMeasure) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } else { this.setMeasuredDimension(0, 0); } } 

哈哈,實際上是和Guideline同樣的嘛,仍是能夠忽略其帶來的性能消耗嘛.上面的mUseViewMeasure一直是false,因此長寬一直爲0.

因此咱們能夠將Guideline,Barrier,Group視爲虛擬試圖,由於它們幾乎不會帶來多的繪製性能損耗.我是這樣理解的.

十7、Optimizer優化(add in 1.1)

能夠經過將標籤app:layout_optimizationLevel元素添加到ConstraintLayout來決定應用哪些優化。這個我感受還處於實驗性的階段,暫時先別用..哈哈

使用方式以下:

<android.support.constraint.ConstraintLayout 
    app:layout_optimizationLevel="standard|dimensions|chains" 
  • none:不優化
  • standard:默認,僅優化直接和障礙約束
  • direct:優化直接約束
  • barrier:優化障礙約束
  • chain:優化鏈條約束
  • dimensions: 優化維度測量,減小匹配約束元素的度量數量

【附】相關架構及資料

資料免費領取方式:查看我主頁

相關文章
相關標籤/搜索