Android ConstraintLayout 使用详解

作者: aidlFor | 来源:发表于2018-07-19 22:03 被阅读108次

    Android在遇到复杂布局的时候,避免不了嵌套布局,渲染起来也影响了应用的性能,而且在维护的时候会有些烦躁,至少我是这样,还有百分比等,这时我们的 ConstraintLayout 就是为了解决这些问题而来的,况且还兼容到API 9,更没有理由了。所以这篇用法就应运而生了,毕竟优秀的东西总是让人向往。(本文不讲解拖拽用法)

    官方文档 https://developer.android.com/reference/android/support/constraint/ConstraintLayout

    (一)基本属性
    layout_constraintLeft_toRightOf
    layout_constraintRight_toLeftOf
    layout_constraintLeft_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

    可以看到每个属性上有两个方向,可以看第一个就是 Left -> Right,表示当前添加到视图控件以什么相对位置进行摆放。通俗的讲就是你告诉我我以谁为基准来摆放我自己。

    image.png
     <Button
        android:id="@+id/button17"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button17"
        />
    <Button
        android:id="@+id/button16"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button16"
        app:layout_constraintLeft_toRightOf="@id/button17"
         />
    

    以上的例子,换成刚才的说法就是,添加button16的时候,以我button16的左侧边去对应button17的右侧边。
    可能有人会说那Start和End呢?相关Start和End的下面4个属性我觉得等同于左右。我查了一下说是有的语言是从右向左书写的。所以用Start和End能兼容两种形式。官方推荐使用Start和End。

    layout_constraintBaseline_toBaselineOf相当于文字基准线对齐,什么是基准线呢?也就是相当于文字的下划线的位置的一条虚线。

    以上的属性有一点特殊的是所有的值不仅可以设置为控件id,也就是根据id摆放,也可以对父容器进行摆放,也就是ConstraintLayout。这时候设置值为parent。

    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button-A"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    
    6B95D046-1223-42B5-848D-2B54AB5513FE.png

    可以看到在所有的方向都指向parent,ButtonA全屏居中了。因为ButtonA的四个边相对了父容器的四个边摆放。如果四根弹簧当失去横向或者纵向的两根则变成了水平居中或者垂直居中。

    再看外边距的几个属性:
    android:layout_marginStart
    android:layout_marginEnd
    android:layout_marginLeft
    android:layout_marginTop
    android:layout_marginRight
    android:layout_marginBottom

    这些跟其他布局的外边距一样,但是如果没有约束关系,也就是链条连接,设置这个属性是无效的。原因可能是ConstraintLayout没有约束关系,就没必要用ConstraintLayout了吧,这可能是个冷知识。

    继续看一个有特色的外边距:
    layout_goneMarginStart
    layout_goneMarginEnd
    layout_goneMarginLeft
    layout_goneMarginTop
    layout_goneMarginRight
    layout_goneMarginBottom

    也就是说在约束目标控件为Gone的时候,要不要来点边距。例如我们在线性布局中,如果三个按钮按水平摆放。中间的设置为Gone后,两边的控件会贴在一起。完全隐藏了。没有设置边距平滑的机会。而这个外边距就是在目标约束组件设置为Gone的时候才会出现的边距。下面会举例子。

    相关基础的ConstraintLayout用法已经介绍完了,简单的练习一下吧,写一个样例,先看一下效果图:


    1745E0DD-7845-4BA7-8A47-7CB2A8734229.png

    中间的是各种相对关系的例子,上面是layout_goneMarginLeft属性的例子,上面的例子是有两个Button,我隐藏了一个,本身是没有间距的,从图中可以看出来出现了间距。然后看一下布局的完整代码。

    <?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/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button-A"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    
    <Button
        android:id="@+id/buttonB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button-B"
        android:layout_marginRight="20dp"
        app:layout_constraintRight_toLeftOf="@+id/buttonA"
        app:layout_constraintTop_toTopOf="@id/buttonA" />
    
    <Button
        android:id="@+id/buttonC"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button-C"
        android:layout_marginTop="20dp"
        app:layout_constraintTop_toBottomOf="@id/buttonA"
        app:layout_constraintLeft_toLeftOf="@id/buttonA"/>
    
    <Button
        android:id="@+id/buttonD"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:text="Button-D"
        app:layout_constraintLeft_toRightOf="@id/buttonA"
        app:layout_constraintTop_toTopOf="@+id/buttonA"
        />
    
    <Button
        android:id="@+id/buttonE"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button-E"
        android:layout_marginBottom="20dp"
        app:layout_constraintBottom_toTopOf="@id/buttonA"
        app:layout_constraintLeft_toLeftOf="@id/buttonA"
        />
    
    
    <!--在buttonGone1的属性设置为android:visibility="gone" ,buttonGone2的外边距layout_goneMarginLeft生效-->
    <Button
        android:id="@+id/buttonGone1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:text="buttonGone-1"
        />
    
    <Button
        android:id="@+id/buttonGone2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_goneMarginLeft="80dp"
        app:layout_constraintLeft_toRightOf="@id/buttonGone1"
        android:text="buttonGone-2"
        />
    
    </android.support.constraint.ConstraintLayout>
    

    (二)基本特性

    1,偏移(Bias)
    在上文第二张图全屏居中的例子中,你可能发现了布局中有蓝色的弹簧线,他是不是很像拉力呢,像是四条弹簧将按钮平衡的拉到中心的呢,事实上也是这样的。你对父布局的边做为相对目标,而button却很小,无法被拉伸到每个边界,所以button被拉到了居中的位置,在ConstraintLayout中也用这种方式进行全屏居中,水平或垂直居中,这种作用在布局的拉力,我们是可以控制拉力的大小的,也就是我们的主角偏移,先来看两个属性。

    layout_constraintHorizontal_bias
    layout_constraintVertical_bias

    很容易的看出是水平偏移和垂直偏移,那就试试吧。


    29758665-856D-444E-84C7-03759B280254.png
    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button-A"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintHorizontal_bias="0.3"
        app:layout_constraintVertical_bias="0.8"
        app:layout_constraintTop_toTopOf="parent" />
    

    横向偏移了0.3,纵向偏移了0.8,相当于按百分比偏移了控件,这种力只有设置parent才会出现吗?当然不是,可以约束到任何边上,再看一个例子:


    962BB2B3-8496-48E6-A19D-564B0E265223.png

    从图中可以看到,底部按钮的左边约束到父布局,右侧约束给了偏移例子的按钮右侧,这样就会出现这种平衡力,那么按钮应该在中间来平衡这个关系才对。当然了,是我做了手脚,加了偏移,横向偏移0.8 看下代码:

    <Button
        android:id="@+id/button1"
        android:layout_width="201dp"
        android:layout_height="wrap_content"
        android:text="偏移例子"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        />
    
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="底部按钮"
        android:layout_marginTop="112dp"
        app:layout_constraintRight_toRightOf="@+id/button1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@+id/button1"
        app:layout_constraintHorizontal_bias="0.8" />
    

    也就是说你在一个范围内想摆放这个按钮,就可以根据你的相对控件,写出这种平衡力,配合偏移来调整位置。

    如果你想让一个控件的中心上下对齐一个控件的一条边呢,你可以约束这个组件的左侧边和右侧边同时约束你要居中的边,看一下例子:

    A830A12B-0F61-4524-B8F7-D19CBB5DD266.png

    图的上方是一个常规的以控件中心居中,跟以父布局没区别,下面是我们要的,以一条边居中,看一下代码:

    <Button
        android:id="@+id/button1"
        android:layout_width="201dp"
        android:layout_height="wrap_content"
        android:text="偏移例子"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        />
    
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="底部按钮"
        android:layout_marginTop="112dp"
        app:layout_constraintRight_toRightOf="@+id/button1"
        app:layout_constraintLeft_toLeftOf="@+id/button1"
        app:layout_constraintTop_toTopOf="@+id/button1"
         />
    
    <Button
        android:id="@+id/button3"
        android:layout_width="201dp"
        android:layout_height="wrap_content"
        android:text="偏移例子"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        />
    
    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="底部按钮"
        android:layout_marginTop="112dp"
        app:layout_constraintRight_toRightOf="@+id/button3"
        app:layout_constraintLeft_toRightOf="@+id/button3"
        app:layout_constraintTop_toTopOf="@+id/button3"
        />
    

    2,圆心定位(Circular positioning)

    circle1.png circle2.png

    看一个三个属性
    layout_constraintCircle : references another widget id
    layout_constraintCircleRadius : the distance to the other widget center
    layout_constraintCircleAngle : which angle the widget should be at (in degrees, from 0 to 360)

    <Button android:id="@+id/buttonA" ... />
      <Button android:id="@+id/buttonB" ...
      app:layout_constraintCircle="@+id/buttonA"
      app:layout_constraintCircleRadius="100dp"
      app:layout_constraintCircleAngle="45" />
    

    我还没想到有哪些场景能用到。暂时上一个官方的例子在这里吧。表示我来过。

    3,可见性行为(Visibility behavior)
    翻译一下文档。

    ConstraintLayout对控件的特定处理标记为View.GONE。

    像其他的布局一样,设置为GONE的控件不会显示,也不是布局本身的一部分(即如果标记为GONE,它们的实际尺寸将不会更改)

    但就布局计算而言,GONE控件仍然是其中的一部分,具有重要的区别:

    1,对于布局传递,它们的尺寸将被视为零(基本上,它们将被解析为一个点)
    2,如果他们对其他控件有约束,他们仍然会生效,但任何边距都会等于零

    visibility-behavior (2).png

    您可以暂时将窗口控件标记为GONE,而不会破坏布局,这在进行简单的布局动画时尤其有用。

    注意:使用的边距将是B在连接到A时定义的边距。在某些情况下,这可能不是您想要的剩余边距(例如A在其容器侧面有100dp的边距,B只有16dp到A,A标记为已消失,B对容器的边距为16dp)。因此,您可以指定在连接到标记为已消失的窗口小部件时要使用的备用边距值(请参阅上面有关已删除边距属性的部分)。

    4,尺寸约束(Dimensions constraints)

    首先区分一下在ConstraintLayout 中android:layout_width和android:layout_height有什么区别

    image.png
    <Button
        android:id="@+id/btn1"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="200dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
    
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
    
    
    <Button
        android:id="@+id/btn3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="0dp"
        app:layout_constraintTop_toBottomOf="@id/btn2"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />
    

    可以看到主要的区别就是match_parent对应ConstraintLayout的0dp了。官方不推荐在ConstraintLayout中使用match_parent,添加相对父布局位置的约束配合0dp的width和height也能实现效果。


    image.png

    第一种则是设置固定大小,第二种则是0dp,第三种是0dp加上margin。

    继续看一下 约束尺寸的四个属性:
    android:minWidth
    android:minHeight
    android:maxWidth
    android:maxHeight

    很明显是设置宽高的最大值和最小值,当其尺寸设置为WRAP_CONTENT时,ConstraintLayout将使用这些最小和最大尺寸。

     <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="有一个很长的故事~~~~~~~~~~~~"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:maxWidth="40dp"
        android:minWidth="3dp" />
    

    可以看到文字应该是超过了40dp所能容纳的大小,我设置了最大只能40dp。效果图:

    image.png

    5,WRAP_CONTENT : 强制约束( enforcing constraints )

    官方译文:
    如果宽高设置为WRAP_CONTENT,则在1.1之前的版本中,它们将被视为文字宽高 - 这意味着约束不会限制生成的宽高。虽然通常这足够(并且更快),但在某些情况下,您可能希望使用WRAP_CONTENT,但仍然强制执行约束以限制生成的宽高。在这种情况下,您可以添加一个相应的属性:

    app:layout_constrainedWidth=”true|false”
    app:layout_constrainedHeight=”true|false”

    如果设置为WRAP_CONTENT,就是如果文字的宽高改变了,约束就失效了,比如你的text里面文字太多,直接就把约束搞没了,为什么会有这样的机制呢,因为有的时候我们的需求更关心于显示的文字,看需求而定要不要这个属性,这是我的理解。

    说个例子。

    image.png
    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="有一个的故事"
        />
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@id/btn1"
        app:layout_constraintRight_toRightOf="parent"
        android:text="有一个小和尚"
        />
    

    很常规的一个相对关系,右侧按钮左边相对左侧按钮的右边,右侧按钮右边相对夫布局,如果按刚才的说法,文字增长,会使布局约束失效。


    image.png
    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="有一个的故事"
        />
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@id/btn1"
        app:layout_constraintRight_toRightOf="parent"
        android:text="有一个小和尚,有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚"
        />
    

    可以看到右侧按钮的文本增多,导致约束失效,然后我们试试强制约束的属性app:layout_constrainedHeight=”true。

    image.png
     <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="有一个的故事"
        />
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@id/btn1"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constrainedWidth="true"
        android:text="有一个小和尚,有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚有一个小和尚"
        />
    

    我们就可以根据这个属性来约束强制保证我们的布局。

    6,MATCH_CONSTRAINT (0dp)

    看几个属性:

    layout_constraintWidth_min and layout_constraintHeight_min :
    layout_constraintWidth_max and layout_constraintHeight_max :
    layout_constraintWidth_percent and layout_constraintHeight_percent :

    上面两个和WRAP_CONTENT的最大最小值宽高类似,写一个一样的例子试试。

    image.png
       <Button
        android:id="@+id/btn1"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="有一个很长的故事~~~~~~~~~~~~"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintWidth_max="40dp"
        app:layout_constraintWidth_min="3dp" />
    

    如果不写相对关系的话是出不来的,因为它是0dp。是的,他需要相对关系的支撑。像这个宽高都为0的,四个方向都要写相对关系的。总之存在于这个布局中就要存在相对关系的。

    需要注意的是不仅仅可以设置多少dp,还可以设置app:layout_constraintWidth_min="wrap",这歌wrap代表的是wrap_content。

    还剩下最后一个属性layout_constraintWidth_percent and layout_constraintHeight_percent :

    设置这个属性需要几个前提:

    1, 宽高应设置为MATCH_CONSTRAINT(0dp)
    2,默认值应设置为app app:layout_constraintWidth_default =“percent”或app:layout_constraintHeight_default =“percent”
    (注意:这在1.1-beta1和1.1-beta2中是必需的,但是如果定义了percent属性,则在以下版本中不需要)
    3,然后将layout_constraintWidth_percent或layout_constraintHeight_percent属性设置为0到1之间的值来定义百分值。

    总结一句话,通俗的讲就是设置自己的尺寸是父容器的多少百分值,需要按上述步骤来写。

    来看一下例子:

    image.png
    <Button
        android:id="@+id/btn1"
        android:layout_width="0dp"
        app:layout_constraintWidth_default="percent"
        app:layout_constraintWidth_percent="0.8"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:text="有一个的故事"
        />
    

    占用了父控件的80%,没什么好说的。

    7,比率 (Ratio)

    先看一下属性:
    app:layout_constraintDimensionRatio

    这个比率的意思是当前自身控件的横纵比,也就是宽和高的比可以通过一个比值来控制。前提是至少需要宽或者高为0dp,以这种形式设置width:height;

    还有一种形式,先看一下文档是如何描述的。

    如果两个尺寸都设置为MATCH_CONSTRAINT(0dp),也可以使用比率。在这种情况下,系统设置满足所有约束的最大尺寸并保持指定的纵横比。要根据另一个特定边的尺寸约束一个特定边,可以预先附加W或H,分别约束宽度或高度。例如,如果一个宽高受两个目标约束(例如,宽度为0dp并且以父对象为中心)你可以通过在比率前添加字母W(用于约束宽度)或H(用于约束高度)来指示哪一边应该被约束,用逗号分隔:

    也就是说宽铺面屏幕,按16来算,高度根据16的9份来展示。

    看一下例子:

    image.png
    <Button
        android:id="@+id/btn1"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:text="有一个的故事"
        />
    
    <Button
        android:id="@+id/btn2"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintDimensionRatio="h,16:9"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        android:text="banner"
        />
    

    7,链 (Chains)

    image.png

    链在单个轴(水平或垂直)上提供类似行的行为。另一个轴可以独立约束

    链由链的第一个元素(链的“头部”)上设置的属性控制:

    image.png

    首先,我们之前讲到的都是单向的相对关系,如果互相建立相对关系,就是链,链分水平链和垂直链,无论是水平和垂直第一个控件就叫做链头,水平就是最左是链头,垂直就是最上是链头。如果给链的设置magin,则整个链都会响应这个边距,然后用剩余空间去调整这个链。

    看你一下例子:

    image.png
    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/btn2"
        android:layout_marginLeft="50dp"
        android:text="1"/>
    
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3"
        android:text="1"/>
    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn2"
        app:layout_constraintRight_toRightOf="parent"
        android:text="1"/>
    

    链的几种风格 Chain Style

    image.png

    在链的第一个元素上设置属性layout_constraintHorizo​​ntal_chainStyle或layout_constraintVertical_chainStyle时,链的行为将根据指定的样式更改(默认为CHAIN_SPREAD)

    看几个属性值:

    CHAIN_SPREAD - 元素将展开(默认样式)
    Weighted chain - 在CHAIN_SPREAD模式下,如果控件设置为MATCH_CONSTRAINT(0dp),它们将拆分可用空间
    CHAIN_SPREAD_INSIDE - 与默认类似,但是链的端点将不会展开
    CHAIN_PACKED - 链的元素将被打包在一起。然后,子项的水平或垂直偏差属性将影响打包元素的定位

    通俗的讲就三种模式

    1,spread

    2,spread_inside

    3, packed

    其他的都是一种扩展用法。

    手写一下上面的例子更好的理解:

    image.png
     <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@id/btn2"
        app:layout_constraintHorizontal_chainStyle="spread"
        android:text="spread"/>
    
    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn1"
        app:layout_constraintRight_toLeftOf="@+id/btn3"
        android:text="spread"/>
    <Button
        android:id="@+id/btn3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn2"
        app:layout_constraintRight_toRightOf="parent"
        android:text="spread"/>
    
    
    <Button
        android:id="@+id/btn4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="spread_inside"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn5"
        app:layout_constraintTop_toBottomOf="@+id/btn1" />
    
    <Button
        android:id="@+id/btn5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn4"
        app:layout_constraintRight_toLeftOf="@+id/btn6"
        app:layout_constraintTop_toBottomOf="@+id/btn2"
        android:text="spread_inside"/>
    <Button
        android:id="@+id/btn6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn3"
        app:layout_constraintLeft_toRightOf="@+id/btn5"
        app:layout_constraintRight_toRightOf="parent"
        android:text="spread_inside"/>
    
    <Button
        android:id="@+id/btn7"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="weight-1份"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn8"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintTop_toBottomOf="@+id/btn4" />
    
    <Button
        android:id="@+id/btn8"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn7"
        app:layout_constraintHorizontal_weight="2"
        app:layout_constraintRight_toLeftOf="@+id/btn9"
        app:layout_constraintTop_toBottomOf="@+id/btn5"
        android:text="weight-2份"/>
    <Button
        android:id="@+id/btn9"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="weight-1份"
        app:layout_constraintTop_toBottomOf="@+id/btn6"
        app:layout_constraintHorizontal_weight="1"
        app:layout_constraintLeft_toRightOf="@+id/btn8"
        app:layout_constraintRight_toRightOf="parent"
        />
    
    <Button
        android:id="@+id/btn11"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="packed"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn22"
        app:layout_constraintTop_toBottomOf="@+id/btn7" />
    
    <Button
        android:id="@+id/btn22"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn11"
        app:layout_constraintRight_toLeftOf="@+id/btn33"
        app:layout_constraintTop_toBottomOf="@+id/btn8"
        android:text="packed"/>
    <Button
        android:id="@+id/btn33"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn9"
        app:layout_constraintLeft_toRightOf="@+id/btn22"
        app:layout_constraintRight_toRightOf="parent"
        android:text="packed"/>
    
    <Button
        android:id="@+id/btn44"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="packed-bias0.3"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/btn55"
        app:layout_constraintTop_toBottomOf="@+id/btn11"
        app:layout_constraintHorizontal_bias="0.3"/>
    
    <Button
        android:id="@+id/btn55"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/btn44"
        app:layout_constraintRight_toLeftOf="@+id/btn66"
        app:layout_constraintTop_toBottomOf="@+id/btn22"
        android:text="packed-bias0.3"/>
    <Button
        android:id="@+id/btn66"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/btn33"
        app:layout_constraintLeft_toRightOf="@+id/btn55"
        app:layout_constraintRight_toRightOf="parent"
        android:text="packed-bias0.3"/>
    

    然后我们就能在横向纵向的链中使用这个模式了,注意的是仅仅在链头设置就好了。

    在看一段官方译文:

    在水平链上,如果一个元素定义了10dp的右边距而下一个元素定义了5dp的左边距,则这两个元素之间产生的边距为15dp。

    在计算链用于定位项目的剩余空间时,会同时考虑项目及其边距。剩余空间不包含边距。

    8,约束线 (Guideline)

    暂且就叫约束线吧,约束线就是一个虚线,让我们可以随意的去定义一条线来辅助布局,当然它在布局显示的时候是不可见的,可以水平可以垂直,通android:orientation=""来设置,值为vertical&horizontal。

    看一下属性:
    app:layout_constraintGuide_percent:

    app:layout_constraintGuide_start:

    app:layout_constraintGuide_end:

    image.png
     <android.support.constraint.Guideline
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/guideline"
        app:layout_constraintGuide_percent="0.3"
        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" />
    

    我设置了一个百分比的属性做为例子,start和end类似,设置具体的大小,start就是从左侧开始,end就是从右侧开始。

    再看一个稍微线多一点的官方例子,


    image.png
    <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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <Button
        android:id="@+id/button4"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:text="@string/button"
        android:layout_marginStart="8dp"
        app:layout_constraintLeft_toLeftOf="@+id/guideline"
        android:layout_marginLeft="8dp"
        app:layout_constraintBaseline_toBaselineOf="@+id/button5"
        app:layout_constraintRight_toLeftOf="@+id/guideline3"
        android:layout_marginRight="8dp"
        app:layout_constraintHorizontal_bias="0.9" />
    
    <Button
        android:id="@+id/button5"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button"
        app:layout_constraintRight_toLeftOf="@+id/guideline2"
        android:layout_marginRight="8dp"
        android:layout_marginEnd="8dp"
        app:layout_constraintLeft_toLeftOf="@+id/guideline3"
        android:layout_marginLeft="8dp"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintTop_toTopOf="@+id/guideline4"
        android:layout_marginTop="16dp" />
    
    <Button
        android:id="@+id/button6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button"
        app:layout_constraintRight_toLeftOf="@+id/guideline2"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintRight_creator="1"
        android:layout_marginEnd="5dp"
        android:layout_marginTop="4dp"
        app:layout_constraintTop_toTopOf="@+id/guideline5"
        android:layout_marginRight="8dp"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="@+id/guideline3"
        app:layout_constraintHorizontal_bias="0.5" />
    
    <TextView
        android:id="@+id/textView3"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/guidelines"
        android:textSize="30sp"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintRight_creator="1"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginTop="16dp"
        tools:layout_constraintLeft_creator="1"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginLeft="16dp"
        app:layout_constraintHorizontal_bias="0.0"
        android:layout_marginRight="16dp" />
    
    <Button
        android:id="@+id/button7"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button"
        tools:layout_constraintTop_creator="1"
        android:layout_marginStart="8dp"
        app:layout_constraintTop_toBottomOf="@+id/button6"
        tools:layout_constraintLeft_creator="1"
        app:layout_constraintLeft_toLeftOf="@+id/guideline"
        android:layout_marginTop="0dp" />
    
    <Button
        android:id="@+id/button8"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button"
        tools:layout_constraintTop_creator="1"
        android:layout_marginStart="10dp"
        android:layout_marginTop="8dp"
        tools:layout_constraintLeft_creator="1"
        app:layout_constraintLeft_toLeftOf="@+id/guideline"
        app:layout_constraintTop_toTopOf="@+id/guideline6" />
    
    <android.support.constraint.Guideline
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/guideline"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.15" />
    
    <android.support.constraint.Guideline
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/guideline2"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.85" />
    
    <android.support.constraint.Guideline
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/guideline3"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.5" />
    
    <android.support.constraint.Guideline
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/guideline4"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.15" />
    
    <android.support.constraint.Guideline
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/guideline5"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.50097847" />
    
    <android.support.constraint.Guideline
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:id="@+id/guideline6"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.85" />
    
    </android.support.constraint.ConstraintLayout>
    

    这个属性tools:layout_constraintTop_creator="1",可以忽略。

    9,屏障 (Barrier)
    这个不是LOL里的屏障,只是直译过来的大家要区分开,Barrier类似 Guideline都是虚拟存在的辅助开发的视图。把多个控件看为整体确定一条约束线。Barrier之外的控件跟随Barrier的内容改变而自适应。

    先看一下属性:

    app:barrierDirection="start" 确定Barrier方向可选值有left, right, top, bottom, start, end
    app:constraint_referenced_ids="button1,button2" 确定Barrier的控件集合。

     <android.support.constraint.Barrier
              android:id="@+id/barrier"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              app:barrierDirection="start"
              app:constraint_referenced_ids="button1,button2" />
    
    image.png

    上图方向为Start,在组件集合1,2的开始的地方竖起了屏障,如果是end呢?


    image.png

    在最宽的控件上竖起了屏障,在很多类似的场景下需要嵌套布局,这就是Barrier存在的含义吧。

    如果控件尺寸发生变化,屏障将根据其方向自动移动以获得极限的控件


    image.png

    这种情况我并没有试出来。

    10,动画 (ConstraintSet)
    让布局平滑的切换。直接上一个官方的例子吧:

    Screenshot_1532008110.png Screenshot_1532008113.png

    上面两张图片平滑的动画切换,

    constraintset_example_big.xml

    <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/activity_constraintset_example"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginEnd="24dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="24dp"
        android:onClick="toggleMode"
        android:scaleType="centerCrop"
        android:src="@drawable/lake"
        app:layout_constraintDimensionRatio="h,16:9"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_constraintLeft_creator="1"
        tools:layout_constraintRight_creator="1"
        android:contentDescription="@string/lake_tahoe_image" />
    
    <TextView
        android:id="@+id/textView9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/lake_tahoe_title"
        android:textSize="30sp"
        app:layout_constraintLeft_toLeftOf="@+id/imageView"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/imageView" />
    
    <TextView
        android:id="@+id/textView11"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="@string/lake_discription"
        app:layout_constraintLeft_toLeftOf="@+id/textView9"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/textView9"
        app:layout_constraintRight_toRightOf="@+id/imageView"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="16dp"
        tools:layout_constraintTop_creator="1"
        tools:layout_constraintBottom_creator="1"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintVertical_bias="0.0" />
    
    </android.support.constraint.ConstraintLayout>
    

    constraintset_example_main.xml

    <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/activity_constraintset_example"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:onClick="toggleMode"
        android:scaleType="centerCrop"
        android:src="@drawable/lake"
        app:layout_constraintBottom_toBottomOf="@+id/textView9"
        app:layout_constraintDimensionRatio="w,16:9"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="@+id/textView9"
        tools:layout_constraintBottom_creator="1"
        tools:layout_constraintTop_creator="1"
        android:contentDescription="@string/lake_tahoe_image"
        app:layout_constraintVertical_bias="0.0" />
    
    <TextView
        android:id="@+id/textView9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:text="@string/lake_tahoe_title"
        android:textSize="30sp"
        app:layout_constraintLeft_toRightOf="@+id/imageView"
        app:layout_constraintTop_toTopOf="parent" />
    
    <TextView
        android:id="@+id/textView11"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="24dp"
        android:text="@string/lake_discription"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        tools:layout_constraintBottom_creator="1"
        tools:layout_constraintLeft_creator="1"
        tools:layout_constraintRight_creator="1"
        tools:layout_constraintTop_creator="1" />
    
    </android.support.constraint.ConstraintLayout>
    

    java

    public class ConstraintSetExampleActivity extends AppCompatActivity {
    
    private static final String SHOW_BIG_IMAGE = "showBigImage";
    
    /**
     * Whether to show an enlarged image
     */
    private boolean mShowBigImage = false;
    /**
     * The ConstraintLayout that any changes are applied to.
     */
    private ConstraintLayout mRootLayout;
    /**
     * The ConstraintSet to use for the normal initial state
     */
    private ConstraintSet mConstraintSetNormal = new ConstraintSet();
    /**
     * ConstraintSet to be applied on the normal ConstraintLayout to make the Image bigger.
     */
    private ConstraintSet mConstraintSetBig = new ConstraintSet();
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.constraintset_example_main);
    
        mRootLayout = (ConstraintLayout) findViewById(R.id.activity_constraintset_example);
        // Note that this can also be achieved by calling
        // `mConstraintSetNormal.load(this, R.layout.constraintset_example_main);`
        // Since we already have an inflated ConstraintLayout in `mRootLayout`, clone() is
        // faster and considered the best practice.
        mConstraintSetNormal.clone(mRootLayout);
        // Load the constraints from the layout where ImageView is enlarged.
        mConstraintSetBig.load(this, R.layout.constraintset_example_big);
    
        if (savedInstanceState != null) {
            boolean previous = savedInstanceState.getBoolean(SHOW_BIG_IMAGE);
            if (previous != mShowBigImage) {
                mShowBigImage = previous;
                applyConfig();
            }
        }
    }
    
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(SHOW_BIG_IMAGE, mShowBigImage);
    }
    
    // Method called when the ImageView within R.layout.constraintset_example_main
    // is clicked.
    public void toggleMode(View v) {
        TransitionManager.beginDelayedTransition(mRootLayout);
        mShowBigImage = !mShowBigImage;
        applyConfig();
    }
    
    private void applyConfig() {
        if (mShowBigImage) {
            mConstraintSetBig.applyTo(mRootLayout);
        } else {
            mConstraintSetNormal.applyTo(mRootLayout);
        }
    }
    }
    

    11,运动布局 (motionlayout)

    ConstraintLayout子类,很炫酷,可以让布局动起来,有兴趣的自行了解吧。

    谷歌官方Demo(包括ConstraintLayout和MotionLayout)
    https://github.com/googlesamples/android-ConstraintLayoutExamples

    相关文章

      网友评论

        本文标题:Android ConstraintLayout 使用详解

        本文链接:https://www.haomeiwen.com/subject/zazbpftx.html