结合实例谈谈约束布局的应用

概述

约束布局ConstraintLayout是谷歌2016年I/O大会发布的新型的layout布局,旨在最大化的减少复杂布局的层级嵌套,实现布局的扁平化,提升页面的渲染速度。

在AS 2.2的版本以上,默认为App依赖了最新的约束布局库,在创建布局文件时也会默认为约束布局,这从侧面反映出来官方推荐开发者使用约束布局。

  • 与传统布局相比的优势

更多请看解析ConstraintLayout的性能优势

  • 约束布局的拖拽操作

    约束布局在AS2.2以上版本中支持可视化拖拽操作,自己实际操作后对这种拖拽方式使用起来很不习惯,而且手动拖拽经常会导致某些参数调整不准。所以本文不讲拖拽,如果有需要,请看下面的链接。

    郭神的Android新特性介绍,ConstraintLayout完全解析

同样UI用常规布局和约束布局前后对比

  • 常规布局实现

    为节省空间,省去部分细节代码,主要看布局结构

    可以看到这是比较复杂的布局,所以再一开始写的时候这里最外层用的布局也是相对布局,但写下来依然嵌套最深层需要5层布局。

  • 约束布局进行改造

    可以看到约束布局实现整个效果没有任何嵌套,所有需要嵌套的View全部扁平化处理了,大大提高了布局渲染速度。

约束布局的具体使用

  • 添加依赖

    1
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
  • 相对位置属性

    这个与相对布局很相似,是以某个View布局为约束来控制自己所在的位置的。官方提供了以下属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //这些属性见名知义,非常好理解
    //当前view的左侧位置与目标view左侧位置对齐
    layout_constraintLeft_toLeftOf
    //当前view的左侧位置与目标view右侧位置对齐
    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

    例子:

    这里最上边有一个分割线,下面是一整个横向布局的View

    代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    <?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"
    android:background="@color/white">

    //最顶上的分割线 view的顶部的父布局的顶部对齐
    <View
    android:id="@+id/gap"
    android:layout_width="match_parent"
    android:layout_height="10dp"
    android:background="@drawable/m_home_bbs_space_bar_gray_bottom_half"
    app:layout_constraintTop_toTopOf="parent"/>

    //左上角的推荐的icon在分割线下面 所以是该View的顶部在分割线底部的下面
    <ImageView
    android:id="@+id/id_community_item_recommend"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/m_home_bbs_waterfall_recommend"
    app:layout_constraintTop_toBottomOf="@id/gap"/>

    //用户头像是在分割线下面,同时据布局的左侧呦12dp的距离
    //所以是左侧与父布局的左侧对齐+marginLeft, 顶部在分割线底部的下面
    <ImageView
    android:id="@+id/item_community_head"
    android:layout_width="36dp"
    android:layout_height="36dp"
    android:layout_marginLeft="12dp"
    android:layout_marginTop="12dp"
    android:scaleType="fitXY"
    android:src="@drawable/m_home_bbs_default_head"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/gap"/>

    //用户头像右下角的身份图标
    //所以是底部与头像的底部对齐,右侧与头像的右侧对齐
    <ImageView
    android:id="@+id/item_community_auth"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right"
    android:scaleType="fitXY"
    android:src="@drawable/m_home_icon_v"
    app:layout_constraintBottom_toBottomOf="@id/item_community_head"
    app:layout_constraintRight_toRightOf="@id/item_community_head"/>

    //头像右侧又处于上方的名字
    //所以是左侧在头像的右侧,顶部与头像的顶部对齐
    <TextView
    android:id="@+id/item_community_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/padding_small"
    android:text="12312312"
    android:textColor="#28354C"
    android:textSize="14sp"
    app:layout_constraintLeft_toRightOf="@id/item_community_head"
    app:layout_constraintTop_toTopOf="@id/item_community_head"/>

    //头像右侧又处于下方的提示
    //所以是左侧在头像的右侧,底部与头像底部对齐
    <TextView
    android:id="@+id/item_community_tips"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/padding_small"
    android:text="12312312"
    android:textColor="#a6a8b6"
    android:textSize="12sp"
    app:layout_constraintBottom_toBottomOf="@id/item_community_head"
    app:layout_constraintLeft_toRightOf="@id/item_community_head"/>

    //vip图标在名字的右侧,同时底部又对齐
    <ImageView
    android:id="@+id/item_community_svip"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/padding_small"
    android:layout_marginTop="1dp"
    android:src="@drawable/m_home_bbs_svip"
    android:visibility="visible"
    app:layout_constraintLeft_toRightOf="@id/item_community_name"
    app:layout_constraintTop_toTopOf="@id/item_community_head"/>

    //关注按钮在父布局的右侧 同时是在头像一横栏的中间部位
    //所以右侧与父布局右侧对齐 顶部与头像顶部对齐 底部与头像底部对齐
    <ImageView
    android:id="@+id/community_item_follow"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical|right"
    android:src="@drawable/m_home_bbs_focus"
    app:layout_constraintBottom_toBottomOf="@id/item_community_head"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="@id/item_community_head"/>

    </android.support.constraint.ConstraintLayout>

    一般实现像上面关注按钮位于头像中间的效果,传统布局都会在外面套一层布局,然后设置居中,可以看到约束布局只要顶部和底部与头像对齐就可以了。为什么会这样呢?看下一个特性

  • Bias偏重

    1
    2
    3
    4
    //横向约束的偏重 默认是0.5 
    layout_constraintHorizontal_bias
    //纵向约束的偏重,默认是0.5
    layout_constraintVertical_bias

    在约束布局中,当控件宽度为warp_content、固定值的时候,为控件添加的其实都是约束”Constraint”,这个约束就好像有个橡皮筋一样,但是并不会改变控件的尺寸。上面的关注按钮之所以会居中,其实是因为上下以头像为约束,同时纵向约束的权重默认是0.5所以自然会居中

    再看一个例子

    像这样悬浮的按钮,传统布局外面用帧布局或者相对布局实现,并且传统布局必须要计算距离底部的dp值用来适配布局,而不能准确实现距离底部的百分比。那约束布局就可以使用这个权重属性来完美实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?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">

    //可以看到这里设置纵向约束比为0.9表明距底部10%
    <ImageButton
    android:id="@+id/community_home_edit_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/m_home_transparent"
    android:src="@drawable/m_home_button_float_background"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.9"/>

    </android.support.constraint.ConstraintLayout>
  • 0dp(MATCH_CONSTRAINT)
    上面的属性已经完全可以替代相对布局了,下面说一下不同点,同样地,看个例子。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
    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">

    <Button
    android:id="@+id/id_btn01"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
    android:text="Btn01"/>

    <Button
    android:id="@+id/id_btn02"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_toRightOf="@id/id_btn01"
    android:text="Btn02"/>

    </RelativeLayout>

    如果用约束布局地话,我们把layout_toRightOf="@id/id_btn01",layout_alignParentRight="true"分别替换为app:layout_constraintLeft_toRightOf="@id/id_btn01",app:layout_constraintRight_toRightOf="parent"但是效果却不一样了

    原因:就是上面权重解释的那样。
    如果需要实现上面的相对布局的效果,只需要给btn02的宽度设置为0dp就解决了。
    0dp在约束布局中的意思就是MATCH_CONSTRAINT,意思就是让约束控制这个宽度。

    1
    2
    3
    //当控件设置了0dp,还可以指定控件的宽度或者高度占父控件的百分比 值在0-1之间		
    layout_constrainWidth_percent
    layout_constrainHeight_percent
  • layout_constraintDimensionRatio宽高比

    1
    2
    3
    //实现控件的宽高比 宽度或者高度必须有一个是0dp
    //这里的值可以为数字a:数字b 默认是宽高比 如果想指定高比宽 数字a前面H,
    layout_constraintDimensionRatio ​

    例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?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">

    //layout_constraintWidth_percent=0.5 意思是宽度为父布局的一半,前提是宽度为0dp
    //layout_constraintDimensionRatio="4:3" 宽高比4:3
    <Button
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintDimensionRatio="4:3"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintWidth_percent="0.5"/>

    </android.support.constraint.ConstraintLayout>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?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">
    //高度固定,宽度0dp,宽高比1:2
    <Button
    android:layout_width="0dp"
    android:layout_height="100dp"
    app:layout_constraintDimensionRatio="h,2:1"
    app:layout_constraintLeft_toLeftOf="parent"/>

    </android.support.constraint.ConstraintLayout>

  • chains与weight

    先看张常见的布局图

    像这样的效果,传统布局的方式都是用线性布局的weight属性实现。而经过前面的介绍,可以发现约束布局已经完全可以替代相对布局了,下面看看上面的效果在约束布局中如何实现。

    chains很像线性布局中的weight属性,但是更为灵活。Chains必须有两个View组成,这两个View相互约束,Chains中的第一个控件叫做链头chain head,Chain Style的样式主要由链头来控制

    看张官方的chain style图

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    <?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">

    //layout_constraintRight_toLeftOf="@id/tv2"
    //button1在button2的左边
    //layout_constraintHorizontal_chainStyle="spread" 链子的样式为平铺,相当于线性布局的weight
    <Button
    android:id="@+id/tv1"
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:background="#f67"
    android:gravity="center"
    android:text="微医"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintHorizontal_chainStyle="spread"
    app:layout_constraintRight_toLeftOf="@id/tv2"/>

    //app:layout_constraintLeft_toRightOf="@id/tv1"
    //button2在button1的右边 这里再声明一遍在Button1的右边,是因为组成链子需要两两约束
    //button2在button3的左边
    <Button
    android:id="@+id/tv2"
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:background="#A67"
    android:gravity="center"
    android:text="问诊"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toRightOf="@id/tv1"
    app:layout_constraintRight_toLeftOf="@id/tv3"/>

    //button3在Button2的右边
    //3个button两两形成约束,组成一个chain,链子的样式由链头控制
    //
    <Button
    android:id="@+id/tv3"
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:background="#767"
    android:gravity="center"
    android:text="社区"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toRightOf="@id/tv2"
    app:layout_constraintRight_toRightOf="parent"/>

    </android.support.constraint.ConstraintLayout>

    chain style除了默认的spread,还有其他两种packed、spread_inside

    这两种效果,分别看下:

    1. spread + 宽度非0

    2. packed + 宽度非0

    3. spread_inside + 宽度非0

    更多的效果看下官方的图

  • Guideline基线

    还是先来张图

    上面我们是通过bais来实现的,约束布局其实还可以通过基线来实现。
    Guideline主要用于辅助布局,即类似为辅助线,横向的、纵向的。并且它是不会显示到界面上的。
    Guideline有以下几个属性

    1
    2
    3
    4
    5
    6
    7
    8
    //基线的方向 分为横向和纵向
    orientation
    //基线占ConstraintLayout的宽或高的百分比
    layout_constraintGuide_percent
    //基线距离ConstraintLayout的左侧或者顶部的距离
    layout_constraintGuide_begin
    //距离ConstraintLayout的右侧或者底部的距离
    layout_constraintGuide_end

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <?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">

    //声明一个纵向的基线,左侧距离父布局80%的宽度
    <android.support.constraint.Guideline
    android:id="@+id/guide1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.8"/>

    //声明一个横向的基线,顶部距离父布局80%的宽度
    <android.support.constraint.Guideline
    android:id="@+id/guide2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.8"/>

    //位于纵向基线的右侧 横向基线的下侧
    <ImageButton
    android:id="@+id/community_home_edit_button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/m_home_transparent"
    android:src="@drawable/m_home_button_float_background"
    app:layout_constraintLeft_toRightOf="@id/guide1"
    app:layout_constraintTop_toBottomOf="@id/guide2"/>

    </android.support.constraint.ConstraintLayout>

    效果图

  • Barrier边界

    Barrier边界也是一个用来辅助布局的控件,它的作用是限制其所引用的一组View边界

    这里看一个网上的例子。

    上面的布局是左侧有两个textview,上部的为tv1,下部的为tv2),tv1和tv2的宽度不定,而tv3要处于他们俩的右侧。但是究竟以谁为准呢?不好确定。图中的tv3是在tv1的右边。那么此时如果tv2的内容过长,就会出现tv2的文字盖在了tv3的上面。

    这个问题可以通过Barrier来解决。

    1
    2
    3
    4
    5
    6
    7
    8
    //设置 Barrier 所创建的位置 值有left、start、right、end、top、 bottom 
    //给约束目标指定对齐的方向
    barrierDirection
    //指定Barrier引用的view的ID,以逗号隔开,无需加 @id
    constraint_referenced_ids
    //默认为true,,即当 Barrier 引用的控件被 GONE 掉时,则 Barrier默认的创建行为
    //是在已 GONE 掉控件的已解析位置上进行创建。如果设置为 false,则不会将 GONE 掉的控件考虑在内。
    barrierAllowsGoneWidgets

    解决上述问题的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?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">

//创建一个在tv1和tv2右侧Barrier,tv3和该Barrier对齐这样tv3就一定会在tv1和tv2的右边了
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="tv1, tv2"/>

<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="123123123"
android:textSize="15sp"
app:layout_constraintLeft_toLeftOf="parent"/>

<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿"
android:textColor="@color/circleColor"
android:textSize="15sp"
android:visibility="visible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv1"/>

<TextView
android:id="@+id/tv3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"
app:layout_constraintLeft_toLeftOf="@id/barrier"/>

</android.support.constraint.ConstraintLayout>
  • Group控件组

    在日常的需求开发中,经常会碰到要对某些View同时进行显示或者隐藏。传统的做法要么是获取这些View的引用,逐个进行设置,要么是外面套层ViewGroup统一实现。但后面这种实现方式有很大的局限性,第一需要隐藏的View可以统一外面包层布局,第二增加了布局的复杂性,影响性能。这种问题,约束布局就能很好的解决。

    Group用来控制一组控件的可见性

    1
    2
    //指定所引用控件的 id。
    constraint_referenced_ids

    如果有多个 Group,是可以同时指定相同的控件的,最终是以 XML 中最后声明的 Group 为准。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     <?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/tv1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="123123123"
    android:textSize="15sp"
    app:layout_constraintLeft_toLeftOf="parent"/>

    <TextView
    android:id="@+id/tv2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿"
    android:textColor="@color/circleColor"
    android:textSize="15sp"
    android:visibility="visible"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toBottomOf="@id/tv1"/>

    //引用tv1,tv2,统一隐藏
    <android.support.constraint.Group
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:visibility="gone"
    app:constraint_referenced_ids="tv1,tv2"/>

    </android.support.constraint.ConstraintLayout>
  • Placeholder占位符

    Placeholder (占位符)用于和一个视图关联起来,通过 setContentId() 方法将占位符转换为指定的视图,即视图将在占位符所在位置上显示,如果此时布局中已包含该视图,则视图将从原有位置消失。除此之外,还可以通过 setEmptyVisibility() 方法设置当视图不存在时占位符的可见性。

    例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?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">

    //设置一个点击事件
    <ImageView
    android:id="@+id/logo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/gh_cm_wy_logo"
    android:onClick="logoClick"/>

    //创建一个占位符,用来替换logo
    <android.support.constraint.Placeholder
    android:id="@+id/placeholder"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"/>

    </android.support.constraint.ConstraintLayout>

    点击图片前,logo在屏幕的左上角效果图


    1
    2
    3
    4
    public void logoClick(View view) {
    Placeholder placeholder = findViewById(R.id.placeholder);
    placeholder.setContentId(R.id.logo);
    }

    点击图片后,会发现logo处于屏幕中间了。图就不贴了。
    另外setContentId也可以在XML中直接给placeholder设置。

    1
    2
    //直接xml设置,相当于提前触发了点击事件
    app:content="@id/logo"

    点击图片后,会发现logo处于屏幕中间了。图就不贴了。
    另外setContentId也可以在XML中直接给placeholder设置。

    1
    2
    //直接xml设置,相当于提前触发了点击事件
    app:content="@id/logo"
  • goneMargin隐藏边距

    这个属性比较有意思,是当一个View与另一个View绑定后,另一个View的属性设置为了Gone,则该属性会生效。
    当设置了goneMargin属性时候,约束的控件如果不是GONE的时候,则不会生效;
    当goneMargin属性和margin属性同时存在的时候,margin属性不会生效。

    1
    2
    3
    4
    5
    6
    7
    8
    //隐藏开始边距 同left
    layout_goneMarginStart
    //隐藏结束边距 同right
    layout_goneMarginEnd
    layout_goneMarginLeft
    layout_goneMarginTop
    layout_goneMarginRight
    layout_goneMarginBottom

    例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?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">

    <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="button3"
    android:visibility="visible"/>

    //Button2在button1的右边,button2以button1为约束,button2设置了goneMarginLeft
    //当Button1可见的时候,android:layout_marginLeft="40dp"会生效,而GoneMargin不会生效
    //当Button1隐藏的时候,正好相反,两者不会同时生效
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="button4"
    app:layout_constraintLeft_toRightOf="@id/btn1"
    app:layout_goneMarginLeft="20dp"
    android:layout_marginLeft="100dp"/>

    </android.support.constraint.ConstraintLayout>

    Button1可见时



    Button1隐藏时

  • Visibility behavior可见性行为

    当一个控件设置为GONE的时候,在布局计算的时候仍会加进去。在布局过程中,将被解析成一个点,所有的margin也将为0,但是对于其他控件的约束仍然存在。

    还是看上面的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?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">

    <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:text="button1"
    android:visibility="visible"
    app:layout_constraintLeft_toLeftOf="parent"/>

    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="100dp"
    android:text="button2"
    app:layout_constraintLeft_toRightOf="@id/btn1"/>

    </android.support.constraint.ConstraintLayout>

    button1距屏幕左侧10dp,button2在button1右边100dp。如果button1隐藏,button2的位置会往左偏移,变成距离屏幕左侧100dp。如何保持button2的位置呢?

    button2就必须设置一个goneMarginLeft= button1.with + button1.marginLeft + button2.marginLeft

  • Enforcing constraints强制约束

    1
    2
    3
    4
    5
    6
    7
    8
    //设置控件的最大宽度
    layout_constraintWidth_max
    //设置控件的最大高度
    layout_constraintHeight_max
    //设置的最大高度是否生效
    layout_constrainedHeight
    //设置的最大宽度是否生效
    layout_constrainedWidth

    在1.1版本之前,对控件设置了warp_content,如果再设置控件的最大宽度、最大高度这样的约束是无效的。再1.1版本,可以添加强制约束,保证其生效。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <?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">

    //button1未设置强制约束,运行后会发现设置的最大宽度和最大高度是无效的
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:text="无效max"
    app:layout_constraintHeight_max="40dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintWidth_max="50dp"/>

    //button2设置了强制约束,生效。
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:text="有效max"
    app:layout_constrainedHeight="true"
    app:layout_constrainedWidth="true"
    app:layout_constraintHeight_max="50dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintWidth_max="50dp"/>

    </android.support.constraint.ConstraintLayout>

  • Circular Positioning圆形定位

    约束布局除了可以使用相对布局类似的属性定位,还提供了额外的圆形定位。

    1
    2
    3
    4
    5
    6
    //参照View的ID
    app:layout_constraintCircle
    //对齐的角度(顺时针方向,0~360度)
    app:layout_constraintCircleAngle
    //与参照View之间的距离
    app:layout_constraintCircleRadius

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?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">
    //button1是参照View,处于屏幕中间
    <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="参照View"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>

    //button2参照button1,位于button1135度,长度未100dp的地方
    <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="定位View"
    app:layout_constraintCircle="@id/btn1"
    app:layout_constraintCircleAngle="135"
    app:layout_constraintCircleRadius="100dp"/>

    </android.support.constraint.ConstraintLayout>


参考链接

Android ConstraintLayout 最新使用小结
Android 约束布局(ConstraintLayout)1.1.0 版详解
约束布局(ConstraintLayout)1.1.2 版本的新特性
Android ConstraintLayout的使用
ConstraintLayout 完全解析 快来优化你的布局吧
Android 约束布局(ConstraintLayout)详解