自去年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的右边对齐,效果图
那么当textView2属性设置为
app:layout_constraintTop_toBottomOf="@+id/textView1"
app:layout_constraintLeft_toRightOf="@+id/textView1"
效果图:
当textView1设置属性 app:layout_constraintRight_toLeftOf="parent"
textView2设置 app:layout_constraintLeft_toLeftOf="parent"时,
效果图:
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左边和右边都有一个拉力,由于默认这个力大小相同就到显示到中间位置。
在上面由于两个相反的约束,而使组件居中,在协同布局中还提供了bias,通过属性layout_constraintHorizontal_bias或者layout_constraintVertical_bias设置组件偏向水平或者垂直的某一测,,默认情况下该值是0.5(50%).例如我们设置textview属性
app:layout_constraintHorizontal_bias="0.3"
此时组件偏向左边
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
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就是该链的链头
链的模式
对于链头我们可以通过属性layout_constraintHorizontal_chainStyle(layout_constraintVertical_chainStyle)设置,该属性有三个值,spread ,spread_inside ,packed
- CHAIN_SPREAD
默认情况下该属性默认值是spread 。它的间隙将平分剩余空间,如上图所示 - CHAIN_SPREAD_INSIDE
spread_inside值会把两边最边缘的两个 View 靠边显示,然后让剩余的 Views 在剩余的空间内平分间隙。当面设置spread_inside时效果图如下
- CHAIN_PACKED
还有一种值是packed ,它表示将view之间紧挨着显示,并且全体居中显示,设置该模式后效果图如下
除此之外,我们可以通过设置layout_constraintHorizontal_bias属性来调整整体的位置。默认情况居中,也就是该值为0.5,例如我们将该值设置0.3,则实现效果如下
- Weighted chain
在官方文档中还介绍了一种模式是权重模式,在CHAIN_SPREAD 模式中,如果我们设置控件的宽或者高设置MATCH_CONSTRAINT即0dp,它们将按权重平分占满父控件的宽或者高,对于权重的设置是属性和我们线性布局设置权重达到一样的效果,例如在上面的三个TextView,我们设置宽度都为0dp,并设置链样式为spread(或spread_inside),此时效果 图如下
如果我们给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的约束位置。
好了今天ConstraintLayout的基础知识就介绍到这里了,下一篇文章将介绍ConstraintLayout布局中Behavior的相关知识,如文中有错误欢迎指出。Hava a wonderful day。