ConstraintLayout控件使用全攻略
标簽(空格分隔): Android
文章目錄
- ConstraintLayout控件使用全攻略
-
- 介紹
- 位置限制
-
- **1.相對位置**
- **2.強制限制**
- **3.基線對齊**
- **4.圓心定位**
- **5.百分比限制**
- **6.Chains(鍊)**
- **7.指定控件寬高百分比大小**
- **8.goneMargin(隐藏邊距)**
- **9.指定寬高比**
- 輔助類
-
- Group群組
- Guideline
- Barrier屏障
- 參考
介紹
ConstraintLayout是Google的一個拖拽布局ViewGroup,釋出已經由來已久了,甚至建立Activity的頁面布局預設就是ConstraintLayout,可見Google對它的推薦态度。總之一句話,用了它各種強大的限制功能,能大大減少布局潛逃,達到布局優化的效果。
在使用ConstraintLayout之前,先進行靈魂三問:ConstraintLayout是什麼?為什麼要使用ConstraintLayout,它有什麼好處?怎麼用?
是什麼?
限制布局ConstraintLayout 是一個ViewGroup,可以在Api9以上的Android系統使用它,它的出現主要是為了解決布局嵌套過多的問題,以靈活的方式定位和調整小部件。從 Android Studio 2.3 起,官方的模闆預設使用 ConstraintLayout。
為什麼用ConstraintLayout?
ConstraintLayout的初衷是減少布局嵌套,再複雜的布局也可能由一個ConstraintLayout搞定,再加上近幾年支援的屬性越來越豐富,而且也是Google極力推薦的,ConstraintLayout在今後的開發和優化中一定會越來越不可或缺。甚至可以在一定程度上适配更多的螢幕尺寸。
怎麼用?
↓↓↓
本篇所舉例子和屬性都是基于2.0.0的beta版,引入方式:
dependencies{
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'
}
位置限制
1.相對位置
相對位置是ConstraintLayout最基本的屬性,類似于RelativeLayout的相對布局,控制子控件的相對位置。
說起來比較抽象,舉個例子:
像這樣的布局需要在代碼中這樣寫:
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_1" />
<Button
android:id="@+id/btn_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="C"
app:layout_constraintLeft_toRightOf="@+id/btn_1"
app:layout_constraintTop_toTopOf="parent" />
可見位置的控制主要是
app:layout_constraintTop_toBottomOf="@+id/btn_1"
屬性實作的,比如這句屬性表示:該View的頂部對齊至目标View的底部。
該系列屬性大概有12個,分别控制View的top,bottom,left,right,start,end
app:layout_constraintTop_toTopOf="@+id/xxx"
app:layout_constraintTop_toBottomOf="@+id/xxx"
app:layout_constraintBottom_toBottomOf="@+id/xxx"
app:layout_constraintBottom_toTopOf="@+id/xxx"
app:layout_constraintLeft_toLeftOf="@+id/xxx"
app:layout_constraintLeft_toRightOf="@+id/xxx"
app:layout_constraintStart_toStartOf="@+id/xxx"
app:layout_constraintStart_toEndOf="@+id/xxx"
app:layout_constraintRight_toRightOf="@+id/xxx"
app:layout_constraintRight_toLeftOf="@+id/xxx"
app:layout_constraintEnd_toEndOf="@+id/xxx"
app:layout_constraintEnd_toStartOf="@+id/xxx"
其中parent代表父控件,比如讓View居中可以這樣寫:
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
2.強制限制
如果要實作一個這樣的布局:
使用ConstraintLayout實作起來代碼是這樣的:
<Button
......
android:id="@+id/btn_a"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_b"
......
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_a"
app:layout_constraintTop_toBottomOf="@+id/btn_a" />
本來可以開心的摸魚了,但有一天A控件的長度變的突然很長,于是出現了下面這樣的情況
B控件并沒有絕對在A的右邊,而且都超出螢幕了,這樣下去肯定不行,于是就要用到強制限制來拯救:
app:layout_constrainedWidth=“true”
這時隻要将上句代碼加入到B控件的xml配置中即可解決,效果:
縱向的緯度對應:
app:layout_constrainedHeight=“true”
3.基線對齊
如果子View是TextView還可以使用基線對齊:
layout_constraintBaseline_toBaselineOf="@+id/xxx"
來寫個例子看下對齊效果:
如果TextView的和大小高度可能不同:
可以看到如果是多行的文字,這個屬性隻以文字第一行為基線進行對齊。
使用這個屬性還是需要注意一下的。
4.圓心定位
圓心定位用一張盜來的圖就可以表達的很清楚:
由圖可見,圓心定位至少有三個屬性來确定限制關系
- app:layout_constraintCircle="@+id/btn_aa" 被限制控件Id
- app:layout_constraintCircleAngle=“0” 圓心偏移角度,豎直正方向方向為0度。
- app:layout_constraintCircleRadius=“68dp” 圓半徑。
由這三個屬性就可以共同使用來進行圓心的限制啦。
下面寫個例子來試試:
<--首選确定一個圓心View-->
<Button
android:id="@+id/btn_aa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="88dp"
android:layout_marginEnd="8dp"
android:text="圓心"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent />
<--限制在圓心0度位置,也就是正上方-->
<View
android:id="@+id/btn_bb"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@color/colorPrimary"
app:layout_constraintCircle="@+id/btn_aa"
app:layout_constraintCircleAngle="0"
app:layout_constraintCircleRadius="68dp" />
<--限制在圓心45度位置,也就是右上方-->
<View
android:id="@+id/btn_cc"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@color/colorPrimary"
app:layout_constraintCircle="@+id/btn_aa"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="68dp" />
來看下效果:
我又在以上代碼上多添加了幾個限制,實作了一個鐘表效果,其中綠色方塊中寫的是限制角度。
5.百分比限制
百分比限制的屬性有垂直百分比和水準百分比,分别是:
app:layout_constraintHorizontal_bias=“0.3”
app:layout_constraintVertical_bias=“0.3”
水準的百分比限制屬性意思是在确定了View的左側限制點和右側限制點之後,此時這個View相對于左右限制點的位置時居中的,也就是app:layout_constraintHorizontal_bias=“0.5”,如圖:
我将View設定為相對于父布局的水準居中位置,也就是
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
此時切換到Design視圖,點選該View,然後再右側可以看到
這樣的顯示,其中紅色箭頭所辨別的50就代表預設的百分比限制位置。此時可以直接拖動這個浮标來修改水準百分比限制,也可以在代碼中添加**app:layout_constraintHorizontal_bias=“0.3”**屬性來修改(直接拖動浮标會生成這句代碼)。
這就是水準百分比限制的作用,同理,豎直方向上的百分比限制也一樣。
寫個例子試試:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="youlookwhat"
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.25" />
此按鈕在相對于父布局水準方向20%,相對于父布局豎直方向25%的位置。
6.Chains(鍊)
一組控件通過雙向限制關聯起來就形成了鍊。如圖:
鍊的兩端一定是限制于parent的,鍊的風格由一條鍊的第一個控件決定:
app:layout_constraintHorizontal_chainStyle=“spread”
layout_constraintHorizontal_chainStyle
有三個枚舉值:
- spread:均勻配置設定鍊所在緯度的所有空間 (預設)
- spread_inside:将第一個元素和最後一個元素放置在邊緣上,并均勻分布其餘元素
- packed:将鍊中元素居中在鍊條的中心
一張圖來說明:
- 注意:一條鍊的屬性是由這條鍊的頭結點控制的。
同時鍊還支援權重,如果将width設定為0dp,并且增加layout_constraintHorizontal_weight屬性來設定權重,就可以建立出一條橫向的權重鍊了。
app:layout_constraintHorizontal_weight=“2” :水準方向上控件所占權重
app:layout_constraintVertical_weight=“1” :豎直方向上控件所占權重
舉個例子,來建立一個水準方向權重為1,2,1的鍊:
<Button
android:id="@+id/btn_1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="窩窩頭"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/btn_2"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="一塊錢四個"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintLeft_toRightOf="@+id/btn_1"
app:layout_constraintRight_toLeftOf="@+id/btn_3"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="嘿嘿"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toRightOf="@+id/btn_2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
效果:
7.指定控件寬高百分比大小
ConstantLayout還可以指定子View的百分比尺寸(太強大了吧)。
一起來見證一下這個屬性:
app:layout_constraintHeight_percent=“0.5” :高相對于父布局的百分比,取值0~1
app:layout_constraintWidth_percent=“0.5” :寬相對于父布局的百分比
舉個例子,将下面的ImageView寬度設為父布局的一半,高度也是父布局的一半:
<ImageView
android:id="@+id/iv_111"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="fitXY"
android:src="@mipmap/hentai"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.5" />
效果:
- 注意:當寬/高設為父布局的百分比後,寬/高也要指定為0dp,否則不會生效。
8.goneMargin(隐藏邊距)
在子view的狀态為gone時,goneMargin設定的邊距開始生效。
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
9.指定寬高比
ConstantsLayout還有一個非常有趣的屬性:
app:layout_constraintDimensionRatio=“w,16:9”
可以通過該屬性來指定子控件的期望寬高比。
舉個例子來看,将一個圖檔寬高比設定為16:9,xml布局:
<ImageView
android:id="@+id/iv_111"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@mipmap/hentai"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="w,16:9"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
來看下效果,左圖是圖檔原始效果,右圖是指定寬高後的亞子
說明一下,layout_constraintDimensionRatio比例字首預設為h,思是寬高比例,例如h,3:2意思為寬3:高2,而指定了屬性w,3:2,意思就成了高寬比,即高3:寬2。
再說明一下,經過我的測試發現若要成功實作期望寬高比,寬或高一方要有固定的值或wrap_content,另一個屬性則要設為0dp,否則該屬性不會生效。
輔助類
Group群組
ConstrantLayout提供了Group來标記多個控件的群組,群組可以指定多個控件,并通過控制Group來實作對這多個控件的操作。
Group群組是一個獨立的View控件,但不會真正顯示在螢幕上,該View最常用的屬性是constraint_referenced_ids,用來指定群組中包含的id。
app:constraint_referenced_ids=“btn_a_1,btn_a_2,btn_a_3” :指定群組包含的控件id。
<androidx.constraintlayout.widget.Group
android:id="@+id/group_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="btn_a_1,btn_a_2,btn_a_3" />
舉個例子,點選讓一個Group群組的控件來顯示隐藏:
java代碼:
public void showHide(View view) {
groupA.setVisibility((groupA.getVisibility() == View.GONE) ? View.VISIBLE : View.GONE);
}
xml代碼:
<Button
android:id="@+id/btn_a_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_a_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_a_1" />
<Button
android:id="@+id/btn_a_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_a_2" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="btn_a_1,btn_a_2,btn_a_3" />
<Button
android:id="@+id/btn_sh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="showHide"
android:text="顯/隐"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
看下效果:
Guideline
輔助線Guideline會在預覽的時候幫助你完成布局,運作時并不會顯示在界面上。
Guideline最常用的兩個屬性是:
android:orientation=“horizontal” :輔助線方向,有兩個枚舉值:horizontal和vertical,顧名思義,代表水準和豎直方向。
app:layout_constraintGuide_percent=“0.5” :輔助線相對父布局百分比位置。
Guideline的使用很簡單,下面舉個例子,來個拍照時常用的黃金分隔輔助線:
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.33" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.66" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.33" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.66" />
效果:
Barrier屏障
顧名思義就是對需要限制的View控件間施加一個水準或豎直的屏障,來實作一對多或多對多的限制關系。
文字太抽象,還是偷張圖來了解:
如圖,這也是經常碰到的需求,一個View要同時再幾個View的右側,并且這幾個View的長度還是可變的,這時無論依賴哪個單獨View都不滿足需求。這時候就需要Barrier屏障來滿足需求了。
Barrier是一個單獨的View,它不會顯示在界面上,可以放心大膽的用。
Barrier的屬性有:
app:constraint_referenced_ids=“btn_1,btn_2” :列出屏障所包含的View的id,對應于圖中左側的三個View。
app:barrierDirection=“right” :Barrier所在View組的位置,有right,left,top和bottom枚舉值可選。
app:barrierMargin=“10dp” :Barrier屏障與控件間間距。
app:barrierAllowsGoneWidgets=“true” :如果Barrier屏障所包含View被設定為Gone,是否影響Barrier位置。如果你不想讓Barrier考慮GONE的view,可以通過将屬性barrierAllowsGoneWidgets設定 為false(預設為true)來更改此設定。
知道了這四個屬性,就可以得心應手的使用Barrier了,舉個例子:
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="grow"
android:text="窩窩頭"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.35" />
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="grow"
android:text="一塊錢四個"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.2"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.65" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierAllowsGoneWidgets="true"
app:barrierDirection="right"
app:barrierMargin="10dp"
app:constraint_referenced_ids="btn_1,btn_2" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="嘿嘿"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/barrier_1"
app:layout_constraintTop_toTopOf="parent" />
點選事件:
public void grow(View view) {
String str = ((TextView) view).getText().toString();
str += "~~~";
((TextView) view).setText(str);
}
上面代碼表示為窩窩頭 和 一塊錢四個 兩個View建立右側屏障Barrier_1,嘿嘿限制在Barrier_1屏障的右側,被屏障包含的窩窩頭和一塊錢四個寬度發生變化時,限制還是關系不會改變。
效果圖:
一不小心就把嘿嘿頂到了視窗外,因為嘿嘿隻有錯側限制了Barrier屏障,并沒有為右側添加限制關系,是以才導緻這個bug,同時也要注意本篇文章所講的強制限制屬性。
軟體開發還是要細心,要有窮盡所有可能情況的習慣,避免低級bug。
參考
https://juejin.im/post/5bac92f2f265da0aba70c1bf#heading-1
https://juejin.im/post/5b013e6f51882542c760dc7b