題外話
關于ConstraintLayout的文章網(wǎng)上一抓一大把务冕,而且ConstraintLayout在16年就已經(jīng)出來了娜搂,但是我一直沒有試著去使用(別問我為什么不去使用,當然是因為懶啊)。畢竟前面的LinearLayout搭配RelativeLayout用習慣了,但是畢竟能減少布局的嵌套。還是要抱著多學習的方式去接觸。所以寫下文章作為總結(jié)。
前言
大家都知道AS在寫相關布局的時候山憨,有二種方式:
1. 拖拽方式
就是在這里進行拖控件查乒,各種操作,因為在以前RelativeLayout和LinearLayout的年代郁竟,自己拖會自動幫我們添加各種屬性值不說玛迄,而且還很不方便,但是對于ConstraintLayout來說添加各種約束在這里操作反而很方便棚亩,而且這里的功能面板也增加了很多新功能蓖议,方便了很多。
當然我也不多說讥蟆,貼上郭霖大神寫得在這里功能面板里面對ConstraintLayout 各種操作方式:
操作面板拖拽方式來使用ConstraintLayout
2.編寫代碼
這種更為大家使用勒虾,而我這里也更多的是直接寫代碼的方式。
正文
控件如何確定自己的位置
1.直接確定控件左上角的坐標
在約束布局中瘸彤,一個控件如何來確定自己的位置呢修然,有人可能說直接寫死讓它在界面的(XXX,XXX)位置不就好了么。
比如在拖拽界面质况,我們把一個TextView拖到了界面中間愕宋。
我們發(fā)現(xiàn)這個TextView的確在中間了,這時候我們看下它的代碼:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我在哪里"
tools:layout_editor_absoluteX="164dp"
tools:layout_editor_absoluteY="263dp" />
</android.support.constraint.ConstraintLayout>
我們發(fā)現(xiàn)了:
tools:layout_editor_absoluteX="164dp"
tools:layout_editor_absoluteY="263dp"
的確我們告訴了TextView的左上角的坐標结榄,這個TextView的確可以確定了位置中贝,但是這二個屬性只是單純的進行演示,在真機操作的時候是無效的臼朗,就像"tools:text"一樣邻寿,可以在寫布局的時候方便查看TextView顯示的文字,但是實際運行app的時候不會有相應內(nèi)容依溯。
而且我們也可以看到布局文件中有錯誤提示老厌,也告訴我們在真實運行時候會跳到(0,0)位置:
This view is not constrained, it only has designtime positions, so it will jump to (0,0) unless you add constraints less...
2.告訴控件相鄰的二個邊的位置情況
如下圖所示:
我們怎么來確定它們的位置?比如我們紅色的矩形A黎炉,我們是不是告訴它:你的左邊靠著外面界面的左邊枝秤,你的頂邊靠著外面界面的頂邊(然后是不是A就處在現(xiàn)在這個位置了)。綠色的矩形B我們可以告訴它:你的右邊靠著外面界面的右邊慷嗜,你的底邊靠著外面界面的底邊(然后B就處在了現(xiàn)在這個位置)淀弹。
所以基本操作就是:確定某個控件二個邊的位置(比如靠在哪個控件旁邊)。
我們來看最簡單的基本操作:
layout_constraint[自己位置]_[目標位置]="[目標ID]"
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
舉個例子:
比如我們A按鈕已經(jīng)確定好位置了庆械。我們現(xiàn)在要放B按鈕薇溃,就像我們上面說的,我們B按鈕的二個邊的位置缭乘,我們可以設置讓B按鈕的左邊靠著A按鈕的右邊(相當于B按鈕的左邊與A按鈕的右邊處于同一位置)沐序。
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintLeft_toRightOf="@+id/buttonA" />
我們可以看到app:layout_constraintLeft_toRightOf="@+id/buttonA"
,B的left
與id
為buttonA
的控件的right
相同位置。所以B的左側(cè)就和A的右側(cè)貼在了一起策幼。
我們發(fā)現(xiàn)上面還有一個layout_constraintBaseline_toBaselineOf
邑时,直接看下圖就可以理解所有相關的屬性:
如果是相對于父布局,我們也可以不寫入另外一個控件的id值特姐,直接填parent值就可以了
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintLeft_toLeftOf="parent"
/>
<android.support.constraint.ConstraintLayout/>
所以以上就是基本的操作晶丘。我們接下來看下其他的特殊屬性。
Margin值相關
比如我們上面的A和B按鈕通過了app:layout_constraintLeft_toRightOf
拼接在一起了唐含,但是我同時希望A和B按鈕中間能空一些距離浅浮,如下圖所示:
我們可以直接使用:
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
這時候就又會有一個問題,如果這時候A的visible
為gone
,這時候B的位置就會自動往左邊了捷枯。因為A的所占的寬度沒有了(但是A在里面對于其他控件的約束性都是還是存在的)
但是如果我的需求就是A隱藏后滚秩,B還是在這個位置(當然有些人可能會說你可以讓B根據(jù)其他控件來確定位置),而且我的B的位置就是根據(jù)A來確定的铜靶。那我們怎么處理叔遂,我們可以設置B的以下屬性,就是當A處于
gone
的時候争剿,我們可以讓B的margin值是根據(jù)以下的屬性值:
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
位置約束不止二個邊
我們上面提過已艰,二個邊的位置確定好了(也可以說二個邊的位置被約束了),我們就可以確定這個控件的相應位置蚕苇,而且還可以通過margin的改變哩掺,來繼續(xù)調(diào)節(jié)控件的位置。那如果我這時候是三個邊約束或者四個邊都約束了呢涩笤,比如:
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
<android.support.constraint.ConstraintLayout/>
我們讓按鈕的左邊與父布局的左邊對齊嚼吞,讓按鈕的右邊與父布局的右邊對齊。這時候因為不是單純的一邊對齊蹬碧,而是相同直線上的二個邊都被約束了舱禽。所以按鈕無法緊靠著左邊的或者右邊的其中一個邊界,所以這時候恩沽,這個按鈕就會居于二個約束邊界的中間位置誊稚。如下圖所示:
也許也有人問,我想在這二個約束條件下時候不是處于正中間罗心,而是處于左邊三分之一的位置里伯,這時候你可以使用:
layout_constraintHorizontal_bias
layout_constraintVertical_bias
分別是水平和垂直方向上的所占比例。
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
</android.support.constraint.ConstraintLayout>
圓形布局
有些需求我們可能需要讓控件以某個控件為中心渤闷,繞著進行布局疾瓮,如下圖所示:
ConstarintLayout自帶了這些功能,我們可以使用:
layout_constraintCircle : 引用另一個控件的id
layout_constraintCircleRadius : 距離另外一個控件中心的距離
layout_constraintCircleAngle : 應該在哪個角度(從0到360度)
例如:
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintCircle="@+id/buttonA"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="45" />
尺寸限制(Dimensions constraints)
1.對ConstraintLayout進行限制:
您可以為ConstraintLayout本身定義最小和最大尺寸:
android:minWidth設置布局的最小寬度
android:minHeight設置布局的最小高度
android:maxWidth設置布局的最大寬度
android:maxHeight設置布局的最大高度
這些最小和最大尺寸將在ConstraintLayout使用
2.對內(nèi)部的控件進行限制:
可以通過以3種不同方式設置android:layout_width
和android:layout_height
屬性來指定控件的尺寸:
- 用特定的值(如123dp等)
- 使用WRAP_CONTENT飒箭,它會要求控件計算自己的大小
- 使用0dp狼电,相當于“MATCH_CONSTRAINT”
WRAP_CONTENT(在1.1中添加)
如果設置為WRAP_CONTENT蜒灰,則在1.1之前的版本中, 約束不會限制生成的尺寸值肩碟。但是在某些情況下卷员,您可能需要使用WRAP_CONTENT,但仍然執(zhí)行約束來限制生成的尺寸值腾务。在這種情況下,你可以添加一個相應的屬性:
應用:layout_constrainedWidth =”真|假”
應用:layout_constrainedHeight =”真|假”
MATCH_CONSTRAINT尺寸(也就是0dp)(在1.1中添加)
設置為MATCH_CONSTRAINT時削饵,默認是大小是占用所有可用空間岩瘦。有幾個額外的修飾符可用:
layout_constraintWidth_min和layout_constraintHeight_min:將設置此維度的最小尺寸
layout_constraintWidth_max和layout_constraintHeight_max:將設置此維度的最大尺寸
layout_constraintWidth_percent和layout_constraintHeight_percent:將設置此維度的大小為父級的百分比
百分比尺寸(Percent Dimensions)
說到Percent Dimensions就不得不說ConstraintLayout中的0dp問題,當控件設置為0dp的時候(0dp的稱呼又叫match_constraint)窿撬,默認的行為是撐開(spread)启昧,占滿可用空間,但是這個行為是可以用layout_constraintWidth_default 屬性來設置的劈伴。在 ConstraintLayout 1.0.x中密末,這個屬性還可以把它設置為wrap。而到了1.1.x跛璧,它又有了一個新的值:percent严里,允許我們設置控件占據(jù)可用空間的百分比。
(注意:這在1.1-beta1和1.1-beta2中l(wèi)ayout_constraintWidth_default是必須的追城,但是如果percent屬性被定義,則在以下版本中不需要刹碾,然后將layout_constraintWidth_percent或layout_constraintHeight_percent屬性設置為介于0和1之間的值)
下面的TextView控件將占據(jù)剩余寬度的50%和剩余高度的50%:
<TextView
android:id="@+id/textView6"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintHeight_default="percent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.5" />
寬高比(Ratio)
您還可以控制控件的height或者width這二個值,讓其中一個值與另外一個值的成特定的比例座柱。為此迷帜,需要至少將一個值設置為0dp(即,MATCH_CONSTRAINT)色洞,并將屬性layout_constraintDimensionRatio設置為給定比率戏锹。例如:
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
這樣這個按鈕的寬和高是一樣大小的。
Ratio可以設置為:
- 浮點值火诸,表示寬度和高度之間的比率
- “寬度:高度”形式的比率
如果兩個維都設置為MATCH_CONSTRAINT(0dp)锦针,則也可以使用比率: 在這種情況下,系統(tǒng)設置滿足所有約束條件的最大尺寸并保持指定的寬高比惭蹂。
為了約束一個特定的邊伞插,可以根據(jù)另一個邊的大小來限定寬度或高度:
可以通過在比率前面添加字母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的比例設置按鈕的高度盾碗,而按鈕的寬度將匹配父布局的約束媚污。
鏈(Chains)
鏈在單個軸(水平或垂直)中提供類似組的行為。
-
創(chuàng)建一個鏈:
如果一組小部件通過雙向連接鏈接在一起廷雅,則認為它們是一個鏈,如下圖所示耗美,是一個具有二個控件的最小的鏈:
-
鏈頭:
鏈由在鏈的第一個元素(鏈的“頭”)上設置的屬性控制:
(頭是水平鏈最左邊的部件京髓,也是垂直鏈最頂端的部件。) -
鏈樣式: 在鏈的第一個元素上設置屬性
layout_constraintHorizontal_chainStyle
或layout_constraintVertical_chainStyle
時商架,鏈的行為將根據(jù)指定的樣式進行更改(默認為CHAIN_SPREAD)堰怨。
例如:
<?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">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AAAA"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/textView3"
/>
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BBBB"
app:layout_constraintEnd_toStartOf="@id/textView4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView2" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CCCC"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView3" />
</android.support.constraint.ConstraintLayout>
效果如下:
屏障 (Barrier)
Barrier是一個虛擬的輔助控件,它可以阻止一個或者多個控件越過自己蛇摸,就像一個屏障一樣备图。當某個控件要越過自己的時候,Barrier會自動移動赶袄,避免自己被覆蓋揽涮。
關于這個控件其他文章有詳細的介紹,我直接附上地址:
ConstraintLayout之Barrier饿肺。
組(Group)
Group幫助你對一組控件進行設置蒋困。最常見的情況是控制一組控件的visibility。你只需把控件的id添加到Group敬辣,就能同時對里面的所有控件進行操作雪标。
<android.support.constraint.ConstraintLayout ...>
<TextView
android:id=”@+id/text1" ... />
<TextView
android:id=”@+id/text2" ... />
<android.support.constraint.Group
android:id=”@+id/group”
...
app:constraint_referenced_ids=”text1,text2" />
</android.support.constraint.ConstraintLayout>
此時如果我們調(diào)用group.setVisibility(View.GONE);
那么text1 和 text2 都將不可見。
Guideline
ConstraintLayout的輔助對象的實用程序類溉跃。Guideline不會顯示在設備上(它們被標記為View.GONE)村刨,僅用于布局。他們只能在ConstraintLayout中工作喊积。
指引可以是水平的也可以是垂直的:
垂直指南的寬度為零烹困,它們的ConstraintLayout父項的高度為零
水平指南的高度為零,其ConstraintLayout父項的寬度為零
定位準則有三種不同的方式:
- 指定布局左側(cè)或頂部的固定距離(layout_constraintGuide_begin)
- 從布局的右側(cè)或底部指定固定距離(layout_constraintGuide_end)
- 指定布局的寬度或高度的百分比(layout_constraintGuide_percent)
相應的代碼為setGuidelineBegin(int乾吻,int)髓梅,setGuidelineEnd(int,int)和setGuidelinePercent(int绎签,float)函數(shù)枯饿。
然后控件就可以被Guideline來約束。(換句話就是說弄了一個隱藏的View诡必,來約束我們的控件奢方,我們的控件相對的就更容易進行位置定位)。
限制于垂直Guideline的按鈕示例:
<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">
<android.support.constraint.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/guideline"
app:layout_constraintGuide_begin="100dp"
android:orientation="vertical"/>
<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
app:layout_constraintLeft_toLeftOf="@+id/guideline"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Placeholder
大家具體使用可以看這篇文章:
New features in ConstraintLayout 1.1.x爸舒。
我以下Placeholder內(nèi)容也就轉(zhuǎn)載這個文章里面的例子:
Placeholder顧名思義蟋字,就是用來一個占位的東西,它可以把自己的內(nèi)容設置為ConstraintLayout內(nèi)的其它view扭勉。因此它用來寫布局的模版鹊奖,也可以用來動態(tài)修改UI的內(nèi)容。
用作模版:
我們用Placeholder創(chuàng)建一個名為template.xml的模版:
模版寫好了我們來填充真正的東西涂炎。
我們把剛才定義的模版include到真正的布局文件中忠聚,并且在這個布局文件中添加真實的控件设哗,注意這里的控件無需添加任何約束,因為它們的位置是由Placeholder決定的两蟀。
還有一點就是模版要放在被引用的所有控件之前:
<?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:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.app.androidkt.constraintlayoutb.MainActivity"
tools:showIn="@layout/activity_main">
<include layout="@layout/template" />
<ImageView
android:id="@+id/top_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/place_holder_demo" />
<ImageButton
android:id="@+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:srcCompat="@drawable/ic_save_black_24dp" />
<ImageButton
android:id="@+id/edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_edit_black_24dp" />
<ImageButton
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:srcCompat="@drawable/ic_cancel_black_24dp" />
<ImageButton
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:srcCompat="@drawable/ic_delete_black_24dp" />
</android.support.constraint.ConstraintLayout>
以上就是PlaceHolder的使用場景之一模版功能网梢。
動態(tài)替換:
PlaceHolder還可以在Java代碼中動態(tài)替換自己的內(nèi)容:
public class MainActivity extends AppCompatActivity {
private Placeholder placeholder;
private ConstraintLayout root;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
}
public void onClick(View view) {
placeholder.setContentId(view.getId());
}
}
如果結(jié)合過渡動畫的話,就可以實現(xiàn)一些比較有趣的效果:
public class MainActivity extends AppCompatActivity {
private Placeholder placeholder;
private ConstraintLayout root;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
}
public void onClick(View view) {
TransitionManager.beginDelayedTransition(root);
placeholder.setContentId(view.getId());
}
}
下面是使用PlaceHolder結(jié)合過渡動畫實現(xiàn)的效果:
而這個Demo也是上面那篇文章作者附上的赂毯,Demo地址是PlaceHolder動態(tài)替換
結(jié)語:
還是老話战虏,哪里不對〉程椋可以在留言處寫出來活烙。我會進行更正,哈哈遣鼓。