Android 約束佈局(ConstraintLayout)1.1.0 版詳解

前言

上一篇文章中,咱們對 ConstraintLayout 1.0.2 版進行了詳細的瞭解。而當時說好的 1.1.0 版本的文章卻直到如今纔出來,相隔了很久。其實關於 1.1.0 beta 版的文章早已寫完,但卻一直沒有發佈,這是由於當時擔憂後面的穩定版會和現有的衝突(事實上的確有),因此一直等到上週四,Google 宣佈 ConstraintLayout 1.1.0 穩定版發佈,因而在週末休息時從新整理髮布了這篇文章。java

若是對 ConstraintLayout 不瞭解,而且尚未觀看上篇文章的,強烈建議先觀看完上篇文章,由於本篇只是對上篇的補充。若是有遺漏或錯誤,歡迎各位補充和指正。android

準備

implementation 'com.android.support.constraint:constraint-layout:1.1.0'
複製代碼

Circular Positioning

圓形定位(Circular Positioning)可讓一個控件以另外一個控件的中心爲中心點,來設置其相對與該中心點的距離和角度。 能夠設置的屬性有:git

  • layout_constraintCircle:引用另外一個控件的 id。
  • layout_constraintCircleRadius:到另外一個控件中心的距離。
  • layout_constraintCircleAngle:控件的角度(順時針,0 - 360 度)。

下面以給頭像設置 badge 爲例,演示下其用法:github

<?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"
    tools:context="com.github.airsaid.constraintlayoutdemo.MainActivity">

    <ImageView
        android:id="@+id/img_avatar"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_avatar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/txt_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="VIP"
        android:textColor="#FFFF00"
        android:textStyle="bold"
        app:layout_constraintCircle="@id/img_avatar"
        app:layout_constraintCircleAngle="45"
        app:layout_constraintCircleRadius="30dp" />

</android.support.constraint.ConstraintLayout>
複製代碼

運行結果:bash

Enforcing constraints

在 1.1 版本以前,若是將控件的尺寸設置爲了 WRAP_CONTENT,那麼對控件設置約束(如:minWidth 等)是不起做用的。那麼強制約束(Enforcing constraints)的做用就是,在控件被設置 WRAP_CONTENT 的狀況下,使約束依然生效。app

須要使用到的屬性有:ide

  • app:constrainedWidth="true|false"
  • app:constrainedHeight="true|false"

下面的例子演示了沒有設置強制約束和設置了強制約束的對比:佈局

<ImageView
        android:id="@+id/img_avatar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_avatar"
        app:layout_constrainedWidth="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_max="100dp" />

<ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_avatar"
        app:layout_constrainedWidth="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/img_avatar"
        app:layout_constraintWidth_max="100dp" />
複製代碼

運行結果:性能

Dimensions

1.1 版本中,當控件的尺寸設置爲了 MATCH_CONSTRAINT 時( 0dp),在設置尺寸上又多了二個新的修飾屬性:優化

  • layout_constrainWidth_percent。
  • layout_constrainHeight_percent。

這兩個屬性的做用就是指定當前控件的寬度或高度是父控件的百分之多少。可設置的值在 0 - 1 之間,1 就是 100%。

設置頭像的寬度佔父控件寬度的 80%(父控件佔滿全屏)例子:

<ImageView
    android:id="@+id/img_avatar"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:scaleType="centerCrop"
    android:src="@mipmap/ic_avatar"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintWidth_percent="0.8" />
複製代碼

運行結果:

Margins and chains

在 1.1.0-beta4 版本中(已知),爲鏈中的控件設置 marginRight/End 是無效的(我的感受這應該是個 Bug)。而在 1.1 穩定版中,不管設置右邊距仍是左邊距都是有效果的,會累計計算。而且在計算剩餘空間時,會將邊距一塊兒考慮。

Optimizer

須要知道的是,當咱們使用 MATCH_CONSTRAINT 時,ConstraintLayout 將不得不對控件進行 2 次測量,而測量的操做是昂貴的。

而優化器(Optimizer)的做用就是對 ConstraintLayout 進行優化,對應設置給 ConstraintLauyout 的屬性是:

  • layout_optimizationLevel。

可設置的值有:

  • none:不該用優化。
  • standard:僅優化直接約束和屏障約束(默認的)。
  • direct:優化直接約束。
  • barrier:優化屏障約束。
  • chain:優化鏈約束(實驗)。
  • dimensions:優化尺寸測量(實驗)。

在設置值時,能夠設置多個,如:

app:layout_optimizationLevel="direct|barrier|dimensions"
複製代碼

Barrier

當咱們在佈局時,有時候就會遇到佈局會隨着數據的多少而改變大小的狀況。如下圖爲例:

(圖片來自官方)

經過上圖就能夠發現,當在 A、B 控件的大小都不肯定的狀況下, View3 以誰做爲約束對象都不對。若是以 A 做爲約束對象,那麼當 B 的寬度過寬時就會被遮擋,同理以 B 做爲約束也是如此。

那麼此時,Barrier(屏障)就派上用場了。這是個很是好用的東東,和 GuideLine 同樣,它是一個虛擬的 View,對界面是不可見的。目的就是輔助佈局。

對 Barrier 可使用的屬性有:

  • barrierDirection:設置 Barrier 所建立的位置。可設置的有:bottom、end、left、right、start、top。
  • constraint_referenced_ids:設置 Barrier 引用的控件。可設置多個,設置的方式是:id, id。(無需加 @id/)
  • barrierAllowsGoneWidgets:默認爲 true,即當 Barrier 引用的控件被 GONE 掉時,則 Barrier 默認的建立行爲是在已 GONE 掉控件的已解析位置上進行建立。若是設置爲 false,則不會將 GONE 掉的控件考慮在內。

說再多不如看代碼,仍是以上圖爲例,來看看 Barrier 是如何解決的:

<?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"
    android:padding="16dp"
    tools:context="com.github.airsaid.constraintlayoutdemo.MainActivity">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title"
        android:textSize="16sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/desc"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="This is a descriptive text."
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/title" />

    <android.support.constraint.Barrier
        android:id="@+id/barrier"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:barrierDirection="end"
        app:constraint_referenced_ids="title, desc" />

    <TextView
        android:id="@+id/content"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:text="This is a piece of content that is very long and long very long and long ..."
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/barrier"
        app:layout_constraintTop_toTopOf="parent" />


</android.support.constraint.ConstraintLayout>
複製代碼

運行結果:

另外一種狀況:

完美解決。

Group

Group 的做用就是控制一組控件的可見性。其可以使用到的屬性爲:

  • constraint_referenced_ids:指定所引用控件的 id。

例:

<android.support.constraint.Group
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="gone"
    app:constraint_referenced_ids="title, desc" />
複製代碼

若是有多個 Group,是能夠同時指定相同的控件的,最終是以 XML 中最後聲明的 Group 爲準。

Placeholder

Placeholder(佔位符)是一個虛擬對象,做用和它的名字同樣,就是佔位。

當放置好 Placeholder 後,能夠經過 setContentId() 方法將佔位符變爲有效的視圖。若是視圖已經存在於屏幕上,那麼視圖將會從原有位置消失。

除此以外,還能夠經過 setEmptyVisibility() 方法設置當視圖不存在時佔位符的可見性。

下面的例子演示了佔位符的使用,當點擊頂部頭像時,頂部頭像會消失並在佔位符處顯示:

<?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"
    android:padding="16dp"
    tools:context="com.github.airsaid.constraintlayoutdemo.MainActivity">

    <ImageView
        android:id="@+id/avatar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_avatar"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <android.support.constraint.Placeholder
        android:id="@+id/placeholder"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
複製代碼

運行結果:

總結

經過本篇文章能夠看到, ConstraintLayout 正在不斷的強大、優化中,而且更是推出了優化器來讓性能更出色。那麼,還有什麼理由不用 ConstraintLayout 呢?!

相關文章
相關標籤/搜索