上篇已经讲述过ConstraintLayout可视化布局,拖拽的使用。本篇着重讲解ConstraintLayout对布局性能的优化,使用的是代码编写,功能更加灵活。
要实现上图展示的页面效果,用其他的包括线性布局,相对布局,或者是百分比布局都很难避免布局嵌套的问题。但是如果使用ConstraintLayout就可以实现一层布局展示页面效果。实现代码如下:
<?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/tv_banner"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#765"
android:gravity="center"
android:text="我是16:6的导航栏"
app:layout_constraintDimensionRatio="16:6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="16dp"
android:paddingStart="16dp"
android:text="哈哈哈"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_one"
style="@style/tv_bottom"
android:background="#f67"
android:text="Tab1"
app:layout_constraintEnd_toStartOf="@id/tv_two"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_two"
style="@style/tv_bottom"
android:background="#a67"
android:text="Tab2"
app:layout_constraintEnd_toStartOf="@id/tv_three"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/tv_one" />
<TextView
android:id="@+id/tv_three"
style="@style/tv_bottom"
android:background="#767"
android:text="Tab3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/tv_two" />
<TextView
android:id="@+id/tv_suspension"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#f12"
android:padding="8dp"
android:text="悬浮球"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.92"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.92" />
<TextView
android:id="@+id/tv_baseline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:background="#f66"
android:text="哈哈哈嗝"
app:layout_constraintBaseline_creator="30"
app:layout_constraintBaseline_toBaselineOf="@+id/tv_suspension"
app:layout_constraintEnd_toStartOf="@id/tv_suspension"
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toStartOf="parent" />
<!--TextView在父布局 水平百分之15,竖直百分之95的位置-->
<!--宽度为父布局宽度的百分之13,高度为父布局高度的百分之30,控件宽高的百分比与当前约束无关只与所在父布局相关-->
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#f91"
android:gravity="center"
android:text="我是宽度为父布局13%高度为30%的TextView"
app:layout_constraintBottom_toTopOf="@+id/tv_one"
app:layout_constraintEnd_toStartOf="@+id/tv_baseline"
app:layout_constraintHeight_percent="0.3"
app:layout_constraintHorizontal_bias="0.15"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.95"
app:layout_constraintWidth_percent="0.13" />
<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.65" />
<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.65" />
<TextView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:background="#520"
android:gravity="center"
android:text="辅助线TextView"
app:layout_constraintStart_toEndOf="@id/guideline_v"
app:layout_constraintTop_toBottomOf="@id/guideline_h" />
</android.support.constraint.ConstraintLayout>
下面将逐个解析ConstraintLayout的属性用法:
一、一个居中的TextView
<TextView
android:id="@+id/tv_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="哈哈哈"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
这里面出现了4个以app开头的新属性,
app:layout_constraintStart_toStartOf="parent" 字面理解就是该控件左侧与父布局的左侧对齐
类似的还有以下几个属性
layout_constraintStart_toEndOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
这几个属性可以理解为某个方向的“拉力”,app:layout_constraintStart_toStartOf="parent" 即控件的左边有来自于父布局左侧的拉力,当控件受到父布局上下左右四个方向大小相等的拉力时那这个控件自然就居中显示了。这也就是约束布局的核心“力的约束”。
和RelativeLayout的不同,以下内容源于鸿洋的和RL的差异。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<Button
android:id="@+id/id_btn01"
android:layout_width="100dp"
android:text="Btn01"
android:layout_height="wrap_content" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/id_btn01"
android:text="Btn02"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
那么经过我们刚才的学习,把:layout_toRightOf="@id/id_btn01",layout_alignParentRight="true"
分别替换为: app:layout_constraintLeft_toRightOf="@id/id_btn01",app:layout_constraintRight_toRightOf="parent"
是不是觉得so easy ,但是我们看一下效果图:
是不是和预期有一定的区别,假设你将Btn02的宽度设置的非常大,你会发现更加诡异的事情:
你会发现Btn02,好像疯了一样,我们设置的在btn01右侧,和与parent右侧对齐完全失效了!!!
在当控件有自己设置的宽度,例如warp_content、固定值时,我们为控件添加的都是约束“Constraint”,这个约束有点像橡皮筋一样会拉这个控件,即上面内容所描述的拉力,但是并不会改变控件的尺寸(RL很明显不是这样的)。
例如上例,当btn02的宽度较小时,我们为其左侧设置了一个约束(btn01右侧),右侧设置了一个约束(parent右侧对其),当两个约束同时生效的时候(你可以认为两边都是相同的一个拉力),btn02会居中;当btn02特别大的时候,依然是这两个力,那么会发生什么?会造成左侧和右侧超出的距离一样大。
那么现在大家肯定有些疑问:怎么样才能和上面的RL一样,宽度刚好占据剩下的距离呢(btn01右侧到屏幕右侧的距离)
我们刚才所有的尝试都是在控件自身拥有特定的宽度情况下执行的;那么如果希望控件的宽度根据由约束来控件,不妨去掉这个特定的宽度,即设置为0试试?对!当我们将btn02的宽度设置为0时,一切又变得很完美。
那么这里,你可能会问0值是什么含义,其实在ConstraintLayout中0代表含义:MATCH_CONSTRAINT,看到这个常量,是不是瞬间觉得好理解了一点。此处要注意的是MATCH_CONSTRAINT在xml页面中不被识别,该值应该直接用0来表示。
最后一个问题,MATCH_PARENT哪去了?你可以认为:在ConstraintLayout中已经不支持MATCH_PARENT这个值了,你可以通过MATCH_CONSTRAINT配合约束实现类似的效果。
好了到这里,目前我们看到其已经和RelativeLayout势均力敌了,接下来我们看一下RL做不到的特性。
二、带有合适宽高比的banner广告栏
有时候我们需要在布局顶部展示一个特定的banner图,比如宽度匹配整个父布局,并且宽高比为16:6。看到这里你的第一想法可能是引入百分比布局,那么如何在ConstraintLayout中快速便捷的实现呢?
<TextView
android:id="@+id/tv_banner"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#765"
android:gravity="center"
android:text="我是16:6的导航栏"
app:layout_constraintDimensionRatio="16:6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
宽度为0dp,通过约束使其占满全局。高度为0dp,通过layout_constraintDimensionRatio定义的宽高比确定实际高度。此属性还支持另外的两种写法,有兴趣可以试试
app:layout_constraintDimensionRatio="W,16:6"
app:layout_constraintDimensionRatio="H,16:6"
三、底部的tab实现
说到底部的tab很多人会想到LinearLayout的weight权重属性,那么ConstraintLayout该如何实现呢?
<TextView
android:id="@+id/tv_one"
style="@style/tv_bottom"
android:background="#f67"
android:text="Tab1"
app:layout_constraintEnd_toStartOf="@id/tv_two"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_two"
style="@style/tv_bottom"
android:background="#a67"
android:text="Tab2"
app:layout_constraintEnd_toStartOf="@id/tv_three"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/tv_one" />
<TextView
android:id="@+id/tv_three"
style="@style/tv_bottom"
android:background="#767"
android:text="Tab3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/tv_two" />
layout_constraintHorizontal_weight类似于线性布局的权重功能。
四、展示一个不一样的悬浮球
这里我们用TextView冒充一下其中需要满足以下两个需求,<!--TextView在父布局 水平百分之15,竖直百分之95的位置-->
<!--宽度为父布局宽度的百分之13,高度为父布局高度的百分之30,控件宽高的百分比与当前约束无关只与所在父布局相关-->
我相信你常用的几个布局很难单一不嵌套实现该效果。
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#f91"
android:gravity="center"
android:text="我是宽度为父布局13%高度为30%的TextView"
app:layout_constraintBottom_toTopOf="@+id/tv_one"
app:layout_constraintEnd_toStartOf="@+id/tv_baseline"
app:layout_constraintHeight_percent="0.3"
app:layout_constraintHorizontal_bias="0.15"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.95"
app:layout_constraintWidth_percent="0.13" />
layout_constraintWidth_percent和layout_constraintHeight_percent 字面很容易理解,就是匹配父布局的百分比。
这里多出来了另一个属性bias,前面我们提过约束类似于拉力的效果,那我们怎么控制力的大小呢?
layout_constraintHorizontal_bias=“0.15” 即设置左右两侧间隙比例分别为15%与85%,相当于左侧提供了85N的拉力,右侧提供了15N的拉力,共同作用下实现了以上的效果。同理layout_constraintVertical_bias的效果可以去实践下。
最后还剩下两个边边角角的属性:
layout_constraintBaseline_toBaselineOf 该控件与XX控件基准线对齐
layout_constraintBaseline_creator 这是一个迷之属性,creator接受整型属性值,但该属性在 1.0.0-alpha8 版本暂时未有任何作用,查看源码发现,ConstraintLayout只是对改属性值进行了接受,但是没有做任何处理,相信在后续版本会新增其功能。
五、Guideline辅助线的使用
<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.65" />
<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.65" />
<TextView
android:layout_width="wrap_content"
android:layout_height="30dp"
android:background="#520"
android:gravity="center"
android:text="辅助线TextView"
app:layout_constraintStart_toEndOf="@id/guideline_v"
app:layout_constraintTop_toBottomOf="@id/guideline_h" />
orientation方向属性就不多讲了,还有缺一个属性决定辅助线的位置,以下3种任一都可以
layout_constraintGuide_begin
layout_constraintGuide_end
layout_constraintGuide_percent
begin=30dp,即可认为距离顶部30dp的地方有个辅助线,根据orientation来决定是横向还是纵向;end=30dp,即为距离底部;percent=0.8即为距离顶部80%。
好了到此基本上ConstraintLayout的常用属性就讲完了,如果有不同的见解欢迎留言交流。