一、概述
ConstraintLayout(約束布局),是Google在2016年推出的一種布局,其簡單、扁平化的使用方式,深得廣大開發者的喜愛。這篇文章我們不探討其性能方面的優勢。意在于和大家一起學習ConstraintLayout的相關使用。
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
二、可視化界面的使用
Android Studio 2.2開始提供了對ConstraintLayout可視化的操作。對于可視化操作方面內容。這里大家可以通過郭霖大神的博客進行學習。
三、屬性介紹
屬性介紹是我們ConstraintLayout重點要學習的部分。可視化界面只是簡化了我們使用,對它所使用的屬性,我們要至少知道其意,并可以進行簡單的修改和手寫布局。
1、相對位置屬性
- layout_constraintLeft_toLeftOf :當前View的左側和另一個View的左側位置對齊
- layout_constraintLeft_toRightOf :當前view的左側會在另一個View的右側位置
- layout_constraintRight_toLeftOf :當前view的右側會在另一個View的左側位置
- layout_constraintRight_toRightOf :當前View的右側和另一個View的右側位置對齊
- layout_constraintTop_toTopOf :頭部對齊
- layout_constraintTop_toBottomOf :當前View在另一個View的下側
- layout_constraintBottom_toTopOf :當前View在另一個View的上方
- layout_constraintBottom_toBottomOf :底部對齊
- layout_constraintBaseline_toBaselineOf :文字底部對齊
- layout_constraintStart_toEndOf :同left_toRightOf
- layout_constraintStart_toStartOf :同left_toLeftOf
- layout_constraintEnd_toStartOf :同right_toLeftOf
- layout_constraintEnd_toEndOf :同right_toRightOf
從ConstraintLayout的相對位置屬性可以看出和我們的RelativeLayout很相似,這也是有人將ConstraintLayout成為增強型的相對布局的原因,不過,其實現思想是完全不同的。從下面的例子我們可以看出來。
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btA"
android:layout_width="148dp"
android:layout_height="wrap_content"
android:text="ButtonA"
/>
<Button
android:id="@+id/btB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ButtonB"
android:layout_toRightOf="@id/btA"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
在圖片1-1中,我們使用RelativeLayout布局實現ButtonB在ButtonA的右側并填滿右側空間,我們將ButtonB的width設置為wrap_content,并且使用 android:layout_toRightOf="@id/btA"和android:layout_alignParentRight="true"兩個相對屬性。現在我們將布局改為ConstraintLayout并將相對屬性改為app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_to
EndOf="@+id/buttonB"
<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="wrap_content"
android:text="buttonB"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/buttonA" />
<Button
android:id="@+id/buttonA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="buttonA"
/>
</android.support.constraint.ConstraintLayout>
我們通過對比可以發現ButtonB同樣是wrap_content,但是我們的RelativeLayout會改變我們的View大小。而ConstraintLayout并不會,他會像橡皮筋一樣拉著我們的View使他在我們的右側居中。并不會改變view的大小。當我們的View寬度足夠大的時候超過了我們又側剩余寬度。也只是如1-3一樣,左右兩側的約束一樣大,使之感覺還是居中的。寬度還是我們設置的寬度并未被改變
接下來,如果我們想要讓我們的ConstraintLayout實現的效果和RelativeLayout一樣。官方給我們提供了一個match_constrain也就是0dp,我們為ButtonB的寬度設置為match_constrain(0dp)就可以將我們的ButtonB填滿我們右側的布局了。而官方對match_constrain的解釋為
- Important: MATCH PARENT is not supported for widgets contained in a ConstraintLayout, though similar behavior can be defined by using MATCH CONSTRAINT with the corresponding left/right or top/bottom constraints being set to “parent”.
可以理解為在ConstraintLayout中已經不支持MATCH_PARENT,你可以通過MATCH_CONSTRAINT配合約束實現類似的效果。
理解了上面的內容我們來看一下類似的內容:強制約束。當我們的ButtonB的width設置為wrap_content的時候,而我們的ButtonB的內容過長就會像下圖一樣。導致約束失效。
而為了防止約束失效,在1.1.0版本中新增了一下屬性。
- app:layout_constrainedWidth=”true|false” //默認false
- app:layout_constrainedHeight=”true|false” //默認false
<?xml version="1.0" encoding="utf-8"?>
<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/buttonA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="buttonA"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="buttonbuttonbuttonbuttonbuttonbuttonbutton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/buttonA"
app:layout_constrainedWidth="true"
/>
</android.support.constraint.ConstraintLayout>
2、GoneMargin
- layout_goneMarginStart
- layout_goneMarginEnd
- layout_goneMarginLeft
- layout_goneMarginTop
- layout_goneMarginRight
- layout_goneMarginBottom
我們對Margin這個屬性應該很熟悉,GoneMargin也很簡單,他的含義就是當一個控件和另一個控件綁定后,如果另一個控件設置為Gone則,GoneMargin這組屬性會生效。
<?xml version="1.0" encoding="utf-8"?>
<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/buttonA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="buttonA"
android:visibility="gone"
/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="buttonB"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/buttonA"
app:layout_goneMarginLeft="40dp"
/>
</android.support.constraint.ConstraintLayout>
3、Chains(鏈)
鏈使我們能夠對一組在水平或豎直方向互相關聯的控件的屬性進行統一管理。成為鏈的條件:一組控件它們通過一個雙向的約束關系鏈接起來,首尾對parent約束。 并且鏈的屬性是由一條鏈的頭結點控制的,如下:
Chains Style:鏈的樣式有三種packed,spread_inside,spread
app:layout_constraintHorizontal_chainStyle="packed | spread_inside | spread"
- Spread Chain:Style為spread的時候。
- Spread Chain:Style為spread_inside的時候。
- Weighted Chain:當Style為spread,width為0dp,可以使用Weighted屬性等分(Weighted屬性和線性布局的weighted用法相同)
- Packed Chain 當Style為packed的時候。
- Packed Chain with Bias,當Style為packed,并且設置了Bias屬性。Bias屬性下部分會講解
<?xml version="1.0" encoding="utf-8"?>
<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:id="@+id/bt_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintEnd_toStartOf="@+id/bt_2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/bt_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B"
app:layout_constraintEnd_toStartOf="@+id/bt_3"
app:layout_constraintStart_toEndOf="@+id/bt_1"
app:layout_constraintTop_toTopOf="@+id/bt_1" />
<Button
android:id="@+id/bt_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="c"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/bt_2"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
4、MATCH_CONSTRAINT
上邊我們說過ConstraintLayout取消了MATCH_PARENT由MATCH_CONSTRAINT來代替,默認大小占用所有約束可用空間,并提供了以下屬性輔助我們使用。
- layout_constraintWidth_min 設置最小寬度
- layout_constraintHeight_min 設置最小高度
- layout_constraintWidth_max 設置最大寬度
- layout_constraintHeight_max 設置最大高度
- layout_constraintWidth_percent 設置寬度相對父類寬度百富比
- layout_constraintHeight_percent 設置高度相對父類高度的百分比
5、百分比布局
約束布局支持子控件設置寬高比,前提條件是至少需要將寬高中的一個設置為0dp。為了約束一個特定的邊,基于另一個邊的尺寸,可以預先附加W,或H以逗號隔開。
<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:layout_width="0dp"
android:layout_height="0dp"http://高度基于寬度的尺寸所以高度為0dp,否則百分比屬性無效
android:text="A"
app:layout_constraintDimensionRatio="H,16:9"http://約束高度基于寬度的尺寸。
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
6、Guideline 輔助線
Guideline輔助線是一條寬或高為0dp的一個看不見的線,和LinearLayout一樣都有orientation屬性設置橫向或垂直。有三種定位方式
- layout_constraintGuide_begin 距離父容器起始位置的距離(左側或頂部)垂直的輔助線使用
- layout_constraintGuide_end 距離父容器結束位置的距離(右側或底部)水平的輔助線使用
- layout_constraintGuide_percent 距離父容器寬度或高度的百分比(垂直或水平的輔助線都可以使用)
例:設置一條垂直方向距離父控件左側為100dp的Guideline:
<android.support.constraint.Guideline
android:layout_width="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="100dp"
android:layout_height="wrap_content"/>
7、Group為ConstraintLayout布局中的子控件進行分組,可控制多個布局控件統一的可見性
<android.support.constraint.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"http://設置可見性
app:constraint_referenced_ids="bt1,bt2"http://要管理的控件id
/>
<Button
android:id="@+id/bt1"
android:layout_width="80dp"
android:layout_height="80dp"
android:text="A"
/>
<Button
android:id="@+id/bt2"
android:layout_width="80dp"
android:layout_height="80dp"
android:text="A"
/>
8、Barrier,屏障,在約束布局中,可以將幾個View作為一個整體來使用,讓其他布局關聯約束這個整體。注意設置他的visibility不能控制這個整體的可見性。我們看一個例子。
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓名:"
app:layout_constraintBottom_toBottomOf="@+id/et_name"
app:layout_constraintTop_toTopOf="@+id/et_name"/>
<TextView
android:id="@+id/tv_contract"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="聯系方式:"
app:layout_constraintBottom_toBottomOf="@+id/et_contract"
app:layout_constraintTop_toTopOf="@+id/et_contract"/>
<EditText
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"/>
<EditText
android:id="@+id/et_contract"
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.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_contract"/>
這里我們把A區域中的姓名和聯系方式用Barrier關聯成一個整體。而右側的B區域中的EditText分別關聯約束這個Barrier整體。當Barrier整體中的TextView的寬度發生改變時,Barrier整體也是變化的,右側的B區域的寬度也會隨之改變。
9、Circular positioning (圓形定位)
- 您可以將一個控件的中心以一定的角度和距離約束到另一個控件的中心,相當于在一個圓上放置一個控件。
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
//引用的控件ID
app:layout_constraintCircle="@+id/buttonA"
//圓半徑
app:layout_constraintCircleRadius="100dp"
//偏移圓角度 水平右方向為0逆時針方向旋轉
app:layout_constraintCircleAngle="45" />
10、bias 權重
- 當一個控件擁有水平方向(左右兩個約束)或者垂直方向(上下兩個約束)的時候,我們可以通過app:layout_constraintHorizontal_bias
="0.16"或 app:layout_constraintVertical_bias="0.303"兩個屬性來對兩個方向的約束進行百分比的調整。讓控件按百分比偏向一方。當不設置是默認是0.5居中的效果。
<Button
android:id="@+id/bt1"
android:layout_width="80dp"
android:layout_height="80dp"
android:text="A"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.2" />
參考文章: