項目需求討論 — ConstraintLayout 詳細使用教程

題外話

關於ConstraintLayout的文章網上一抓一大把,並且ConstraintLayout在16年就已經出來了,可是我一直沒有試着去使用(別問我爲何不去使用,固然是由於懶啊)。畢竟前面的LinearLayout搭配RelativeLayout用習慣了,可是畢竟能減小布局的嵌套。仍是要抱着多學習的方式去接觸。因此寫下文章做爲總結。html

前言

你們都知道AS在寫相關佈局的時候,有二種方式:android

1. 拖拽方式

就是在這裏進行拖控件,各類操做,由於在之前RelativeLayout和LinearLayout的年代,本身拖會自動幫咱們添加各類屬性值不說,並且還很不方便,可是對於ConstraintLayout來講添加各類約束在這裏操做反而很方便,並且這裏的功能面板也增長了不少新功能,方便了不少。git

固然我也很少說,貼上郭霖大神寫得在這裏功能面板裏面對ConstraintLayout 各類操做方式: 操做面板拖拽方式來使用ConstraintLayoutgithub

2.編寫代碼

這種更爲你們使用,而我這裏也更多的是直接寫代碼的方式。bash

正文

控件如何肯定本身的位置

1.直接肯定控件左上角的座標

在約束佈局中,一個控件如何來肯定本身的位置呢,有人可能說直接寫死讓它在界面的(XXX,XXX)位置不就行了麼。app

好比在拖拽界面,咱們把一個TextView拖到了界面中間。less

咱們發現這個TextView的確在中間了,這時候咱們看下它的代碼:ide

<?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>
複製代碼

咱們發現了:函數

tools:layout_editor_absoluteX="164dp"
tools:layout_editor_absoluteY="263dp"
複製代碼

的確咱們告訴了TextView的左上角的座標,這個TextView的確能夠肯定了位置,可是這二個屬性只是單純的進行演示,在真機操做的時候是無效的,就像"tools:text"同樣,能夠在寫佈局的時候方便查看TextView顯示的文字,可是實際運行app的時候不會有相應內容。佈局

並且咱們也能夠看到佈局文件中有錯誤提示,也告訴咱們在真實運行時候會跳到(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就處在如今這個位置了)。綠色的矩形B咱們能夠告訴它:你的右邊靠着外面界面的右邊,你的底邊靠着外面界面的底邊(而後B就處在瞭如今這個位置)。

因此基本操做就是:肯定某個控件二個邊的位置(好比靠在哪一個控件旁邊)。

咱們來看最簡單的基本操做: 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按鈕已經肯定好位置了。咱們如今要放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的leftidbuttonA的控件的right相同位置。因此B的左側就和A的右側貼在了一塊兒。

咱們發現上面還有一個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的visiblegone,這時候B的位置就會自動往左邊了。由於A的所佔的寬度沒有了(可是A在裏面對於其餘控件的約束性都是仍是存在的)

可是若是個人需求就是A隱藏後,B仍是在這個位置(固然有些人可能會說你可讓B根據其餘控件來肯定位置),並且個人B的位置就是根據A來肯定的。那咱們怎麼處理,咱們能夠設置B的如下屬性,就是當A處於 gone的時候,咱們可讓B的margin值是根據如下的屬性值:

layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom

複製代碼

位置約束不止二個邊

咱們上面提過,二個邊的位置肯定好了(也能夠說二個邊的位置被約束了),咱們就能夠肯定這個控件的相應位置,並且還能夠經過margin的改變,來繼續調節控件的位置。那若是我這時候是三個邊約束或者四個邊都約束了呢,好比:

<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.對內部的控件進行限制:

能夠經過以3種不一樣方式設置android:layout_widthandroid:layout_height屬性來指定控件的尺寸:

  • 用特定的值(如123dp等)
  • 使用WRAP_CONTENT,它會要求控件計算本身的大小
  • 使用0dp,至關於「MATCH_CONSTRAINT」

WRAP_CONTENT(在1.1中添加)

若是設置爲WRAP_CONTENT,則在1.1以前的版本中, 約束不會限制生成的尺寸值。可是在某些狀況下,您可能須要使用WRAP_CONTENT,但仍然執行約束來限制生成的尺寸值。在這種狀況下,你能夠添加一個相應的屬性:

應用: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,容許咱們設置控件佔據可用空間的百分比。

(注意:這在1.1-beta1和1.1-beta2中layout_constraintWidth_default是必須的,可是若是percent屬性被定義,則在如下版本中不須要,而後將layout_constraintWidth_percent或layout_constraintHeight_percent屬性設置爲介於0和1之間的值)

下面的TextView控件將佔據剩餘寬度的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),則也可使用比率: 在這種狀況下,系統設置知足全部約束條件的最大尺寸並保持指定的寬高比。

爲了約束一個特定的邊,能夠根據另外一個邊的大小來限定寬度或高度: 能夠經過在比率前面添加字母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)

鏈在單個軸(水平或垂直)中提供相似組的行爲。

  • 建立一個鏈: 若是一組小部件經過雙向鏈接連接在一塊兒,則認爲它們是一個鏈,以下圖所示,是一個具備二個控件的最小的鏈:
  • 鏈頭: 鏈由在鏈的第一個元素(鏈的「頭」)上設置的屬性控制:
    (頭是水平鏈最左邊的部件,也是垂直鏈最頂端的部件。)
  • 鏈樣式: 在鏈的第一個元素上設置屬性layout_constraintHorizontal_chainStylelayout_constraintVertical_chainStyle時,鏈的行爲將根據指定的樣式進行更改(默認爲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> 複製代碼

此時若是咱們調用group.setVisibility(View.GONE);那麼text1 和 text2 都將不可見。


Guideline

ConstraintLayout的輔助對象的實用程序類。Guideline不會顯示在設備上(它們被標記爲View.GONE),僅用於佈局。他們只能在ConstraintLayout中工做。

指引能夠是水平的也能夠是垂直的: 垂直指南的寬度爲零,它們的ConstraintLayout父項的高度爲零 水平指南的高度爲零,其ConstraintLayout父項的寬度爲零 定位準則有三種不一樣的方式:

  • 指定佈局左側或頂部的固定距離(layout_constraintGuide_begin)
  • 從佈局的右側或底部指定固定距離(layout_constraintGuide_end)
  • 指定佈局的寬度或高度的百分比(layout_constraintGuide_percent)

相應的代碼爲setGuidelineBegin(int,int),setGuidelineEnd(int,int)和setGuidelinePercent(int,float)函數。

而後控件就能夠被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內容也就轉載這個文章裏面的例子:

Placeholder顧名思義,就是用來一個佔位的東西,它能夠把本身的內容設置爲ConstraintLayout內的其它view。所以它用來寫佈局的模版,也能夠用來動態修改UI的內容。

用做模版: 咱們用Placeholder建立一個名爲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的使用場景之一模版功能。

動態替換: PlaceHolder還能夠在Java代碼中動態替換本身的內容:

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());
  }
}
複製代碼

若是結合過渡動畫的話,就能夠實現一些比較有趣的效果:

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結合過渡動畫實現的效果:

而這個Demo也是上面那篇文章做者附上的,Demo地址是 PlaceHolder動態替換


結語:

仍是老話,哪裏不對。能夠在留言處寫出來。我會進行更正,哈哈。

相關文章
相關標籤/搜索