ConstraintLayout基础介绍

自去年Google I/O 大会发布ConstraintLayout至今,已有一年多的时间,但是并没有普及开来,了解过ConstraintLayout布局的人知道,它的性能的确提升了不少。在前不久,Google 开发者博客发布了一篇文章Understanding the performance benefits of ConstraintLayout中文地址)详细分析ConstraintLayout性能的优势,感兴趣的朋友可以去看看。

当然自己之前也没有认识到ConstraintLayout布局的性能优势,所以从这篇文章开始由浅入深详细介绍ConstraintLayout的属性及使用,也让自己对ConstraintLayout也有一个更全面的认识,今天的这篇文章主要介绍布局的一些属性,学习地址是Google文档

配置

在使用ConstraintLayout之前我们需要在我们app下的gradle文件添加ConstraintLayout依赖,截止到目前ConstraintLayout的最新版本是1.0.2.

dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.2'
}

Relative positioning

相对定位的效果和RelativeLayout布局有异曲同工之处,只不过要比RelativeLayout强大。约束能允许我们指定一个控件相对于另一个控件的位置,通过一些属性我们可以对组件进行水平或者垂直排列。例如当我们使用含有Left, Right, Start 或者End关键词词属性进行定位时即是对组件进行水平方向排列,同理 top, bottom 和 text baseline(文字的基线位置)就是垂直方向排列。具体相对定位的一些属性组合如下

  • 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

对于上面这些属性的值有两种,一种就是同层级组件ID,还有就是parent,当值为parent时即是相对于父布局进行定位。

<?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">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="textView1" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="textView2"
        app:layout_constraintLeft_toRightOf="@+id/textView1" />
</android.support.constraint.ConstraintLayout>

例如上面的布局,我们使用app:layout_constraintLeft_toRightOf="@+id/textView1"将textView2的左边和textView1的右边对齐,效果图

image.png

那么当textView2属性设置为

    app:layout_constraintTop_toBottomOf="@+id/textView1"
    app:layout_constraintLeft_toRightOf="@+id/textView1"

效果图:


image.png

当textView1设置属性 app:layout_constraintRight_toLeftOf="parent"
textView2设置 app:layout_constraintLeft_toLeftOf="parent"时,

效果图:


image.png

Margins

对于margin值我们都不陌生,因为我们经常使用,有以下几种

  • android:layout_marginStart
  • android:layout_marginEnd
  • android:layout_marginLeft
  • android:layout_marginTop
  • android:layout_marginRight
  • android:layout_marginBottom

需要注意的是此margin只对于设置了约束的地方起作用,如下布局

<?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">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="10dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:text="textView1" />
</android.support.constraint.ConstraintLayout>

我们只设置了TextView的左边和父布局左边约束,当我们设置了左边和上边的margin值都为10时,发现只有左边的边距生效,而上边的变化没有发生作用,这也就验证了边距只对有约束行为的地方起作用。

除了我们常用的margin设置属性外,ConstraintLayout 还提供了一些特有的margin设置。先看下面布局

<?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">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="textView1"
        app:layout_constraintLeft_toLeftOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="textView2"
        app:layout_constraintLeft_toRightOf="@+id/textView1" />
</android.support.constraint.ConstraintLayout>

上面textview2在textview1的右边,那么当我们由于某种需求将textview1设置了 View.GONE隐藏该控件,那么此时textview2将跑到位置将显示在左上角,如果我们在隐藏textview时而保持textview2位置不变。在之前的应用中能稍微比较麻烦一点,但是ConstraintLayout 给我们提供了layout_goneMargin**类的属性,该属性是表示约束隐藏时的margin值。例如上面的textView2我们增加app:layout_goneMarginLeft="100dp"属性就可以保持当约束textView1隐藏时而保持textview2位置不变。

需要注意的一点是如果某个约束设置了View.GONE,相当于这个组件宽和高为0,约束布局中相当于一个点,其他设置的约束依然有效。

Centering positioning and bias

    <TextView
        android:id="@+id/textView1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="textView1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

如果约束布局中我们添加textview并设左边和父布局左边,右边和父布局右边对齐,并设置宽度100dp,那么此时效果是怎样的呢。我们发现textview居中显示了,这就是约束布局的居中定位。
其实我们可以理解为父布局对textview左边和右边都有一个拉力,由于默认这个力大小相同就到显示到中间位置。

image.png

在上面由于两个相反的约束,而使组件居中,在协同布局中还提供了bias,通过属性layout_constraintHorizontal_bias或者layout_constraintVertical_bias设置组件偏向水平或者垂直的某一测,,默认情况下该值是0.5(50%).例如我们设置textview属性

        app:layout_constraintHorizontal_bias="0.3"

此时组件偏向左边


image.png

Dimensions constraints

当我们的协调布局的子组件设置wrap_content时,我们可以通过android:minWidth或者android:minHeight 属性设置最小宽度或者高度的约束。minWidth/minHeight只有宽度或者高度为wrap_content才有作用。

对于android:layout_height /android:layout_width属性,它的值只有三种情况

  • 使用指定的大小或者指定大小的引用,如100dp
  • WRAP_CONTENT,此时根据内容自己计算大小
  • 0dp,该值相当于MATCH_CONSTRAINT。

需要注意的是在约束布局中MATCH_PARENT 属性值不在支持,例如在上面的TextView我们设置layout_width分别是100dp,0dp,0dp(marginLeft :20dp)大致效果如图a,b,c

image.png

Ratio

比例约束可以约束我们控件的宽高比,例如下面示例

    <TextView
        android:id="@+id/textView1"
        android:layout_width="100dp"
        android:layout_height="0dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="textView1"
        app:layout_constraintDimensionRatio="2:1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

设置宽度为100dp,高度为0dp,此时设置了宽高比是2:1,则宽度会自动约束调整为50dp。
如果我们将上面的宽和高度调换,宽为0dp,高100dp,此时最终宽度为200,高度100(宽:高=2:1)

在上面介绍的的宽高比约束是单维度的,那么如果我们的宽和高都有约束,都设置为0dp,在这种情况下,系统会使用满足所有约束条件和比率的最大尺寸。当然我们也可以在比例值前面加 W 或者 H 来分别约束宽度或者高度,如H,2:1。

Chains

链是一种特殊的约束它能让多个该链连接的 多个Views 平分剩余空间位置
如下我们创建一个水平链

<?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">

    <TextView
        android:id="@+id/A"
        android:layout_width="100dp"
        android:layout_height="30dp"
        android:background="@color/btnnormal"
        android:gravity="center"
        android:text="A"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/B" />

    <TextView
        android:id="@+id/B"
        android:layout_width="100dp"
        android:layout_height="30dp"
        android:background="@color/colorAccent"
        android:gravity="center"
        android:text="B"
        app:layout_constraintLeft_toRightOf="@+id/A"
        app:layout_constraintRight_toLeftOf="@+id/C" />

    <TextView
        android:id="@+id/C"
        android:layout_width="100dp"
        android:layout_height="30dp"
        android:background="@color/btnnormal"
        android:gravity="center"
        android:text="C"
        app:layout_constraintLeft_toRightOf="@+id/B"
        app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>

上面代码的效果图如下,A和C相对于父组件的左边和右边有一个约束,A和B,B和C之间两两相互约束,我们还需要知道的是对我们称链的第一个元素组件为链头。如下图A就是该链的链头


image.png

链的模式

对于链头我们可以通过属性layout_constraintHorizontal_chainStyle(layout_constraintVertical_chainStyle)设置,该属性有三个值,spread ,spread_inside ,packed

  • CHAIN_SPREAD
    默认情况下该属性默认值是spread 。它的间隙将平分剩余空间,如上图所示
  • CHAIN_SPREAD_INSIDE
    spread_inside值会把两边最边缘的两个 View 靠边显示,然后让剩余的 Views 在剩余的空间内平分间隙。当面设置spread_inside时效果图如下
image.png
  • CHAIN_PACKED
    还有一种值是packed ,它表示将view之间紧挨着显示,并且全体居中显示,设置该模式后效果图如下
image.png

除此之外,我们可以通过设置layout_constraintHorizontal_bias属性来调整整体的位置。默认情况居中,也就是该值为0.5,例如我们将该值设置0.3,则实现效果如下

image.png
  • Weighted chain
    在官方文档中还介绍了一种模式是权重模式,在CHAIN_SPREAD 模式中,如果我们设置控件的宽或者高设置MATCH_CONSTRAINT即0dp,它们将按权重平分占满父控件的宽或者高,对于权重的设置是属性和我们线性布局设置权重达到一样的效果,例如在上面的三个TextView,我们设置宽度都为0dp,并设置链样式为spread(或spread_inside),此时效果 图如下
image.png

如果我们给A和C设置下面属性

        app:layout_constraintHorizontal_weight="1"

给B设置属性

        app:layout_constraintHorizontal_weight="2"

那么此时A,B,C的宽度为1:2:1比例占满父组件宽度。设置权重时链样式不能设置为packed (设置后宽度会收缩为0)

参照线Guideline

Guideline用于辅助我们对View进行定位,以及设置约束,它不会再真正的显示,只是起到辅助作用,常用属性如下

  • android:orientation
    该属性可以指定辅助线是垂直还是水平线,它有两个值即vertical,horizontal,
  • layout_constraintGuide_begin/layout_constraintGuide_end
    用来设置对齐父组件的 start 或者end边缘的距离,
  • layout_constraintGuide_percent
    该值是0.0到1.0之间,用来设置辅助线在父布局的位置,例如设置0.5,就相当于在父视图宽度的中间50%。
    <android.support.constraint.Guideline
        android:id="@+id/guideline_h"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.5" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline_v"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />

    <TextView
        android:id="@+id/tvguide"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="@color/red"
        android:gravity="center"
        android:text="Guide"
        app:layout_constraintBottom_toTopOf="@+id/guideline_h"
        app:layout_constraintLeft_toLeftOf="@+id/guideline_v" />

在上面我们在约束布局中创建两个辅助线,分别是垂直和水平的辅助线,并将textView的下面和水平辅助线的上面对齐,将TextView的左边和垂直辅助线左边对齐。这样我们可以使用辅助线任意控制View的约束位置。

image.png

好了今天ConstraintLayout的基础知识就介绍到这里了,下一篇文章将介绍ConstraintLayout布局中Behavior的相关知识,如文中有错误欢迎指出。Hava a wonderful day。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容