ConstraintLayout的使用
ConstraintLayout的字面意思,约束布局。2016 年 Google I/O 推出。扁平式的布局方式,无任何嵌套,减少布局的层级,优化渲染性能。总之都是他的好,但是到底好在哪?先来学习一下看看。本篇文章是按照我的学习历程下来的。
属性介绍
- 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
基本属性
乍一看觉得和RelativeLayout的属性相似,再一看还是有点区别的。举个例子:
控件B位于控件A的右边,RelativeLayout的写法是layout_toRightOf,而ConstraintLayout的写法是layout_constraintRight_toRightOf和layout_constraintLeft_toRightOf。怎么出现了两个toRightOf,这就是它们的不同之处,理解这个也不难,先来看个图。
对比着这个图再来解释上边的属性,以layout_constraintRight_toRightOf为例:
constraintRight:指当前控件的右边
toRightOf:指位于指定的约束控件的右边
同理,layout_constraintLeft_toRightOf就是指当前控件的左边缘位于指定约束控件的右边。现在是不是有点理解了。实战一下看看
需求一:控件B位于控件A的右边并列显示
分析一下,并列显示就是B的左边缘位于A的右边,代码如下:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tvA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控件A"
android:background="@color/c5a9fe9"
android:padding="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控件B"
android:background="@color/orange"
android:padding="10dp"
app:layout_constraintLeft_toRightOf="@id/tvA"/>
</android.support.constraint.ConstraintLayout>
运行结果如下:
类比一下其他属性也就很好理解了,这里再特殊说明一下layout_constraintBaseline_toBaselineOf的属性。
以上边的代码为例,我们用padding值将控件B撑大,使其大于控件A,如下
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tvA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控件A"
android:background="@color/c5a9fe9"
android:padding="10dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控件B"
android:background="@color/orange"
android:padding="20dp"
app:layout_constraintLeft_toRightOf="@id/tvA"
/>
</android.support.constraint.ConstraintLayout>
运行结果如下:
接下来再将控件B加上代码app:layout_constraintBaseline_toBaselineOf=”@id/tvA”,再看看效果
图中我们可以看到控件B是通过将控件整体上移的形式来让其两个控件的字体对齐的。这里一定要注意!
基本属性的参数值
到这里基本属性的含义已经解释的差不多了。再来看看属性的参数值。上边的例子中说的是子控件和子控件之间的约束。我们知道RelativeLayout可以约束到位于父布局的位置的,所以我们的ConstraintLayout也不能少了子控件与父布局的约束了。
需求:开发中常见的样式,两个控件分别位于屏幕的左右两边
如上图这样的效果,代码如下:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/tvA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="控件A"
android:padding="10dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/new_next_gray"
app:layout_constraintRight_toRightOf="parent"
android:padding="10dp"/>
</android.support.constraint.ConstraintLayout>
我们只是对ImageView加了app:layout_constraintRight_toRightOf=”parent”一行代码,图片就移到了屏幕的右边,是不是也很方便。
关于Margin
在ConstraintLayout布局中只要对控件设置了约束,margin的使用和其他布局的没什么区别,但是多了几个margin的属性:
- layout_goneMarginStart
- layout_goneMarginEnd
- layout_goneMarginLeft
- layout_goneMarginTop
- layout_goneMarginRight
layout_goneMarginBottom
与普通的margin属性多了一个gone,官方解释:When a position constraint target’s visibility is View.GONE, you can also indicates a different margin value to be used using the following attributes
翻译后:当位置约束目标的可见性为View.GONE时,还可以指示使用以下属性使用的不同边距值
仅仅这一句话我并没有理解,但是我知道了和view的可见性有关。我们猜想这个属性是指,当约束控件不可见时,依然保留着设置的margin值。接下来不如写段代码看看吧。
如上图,我们要实现,“已设置”控件位于箭头图片的左边20dp的位置。实现上边布局的代码:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="交易密码"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
android:id="@+id/iv"
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@mipmap/new_next_gray"
android:padding="10dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tvA"
android:layout_width="50dp"
android:layout_height="30dp"
android:text="已设置"
android:gravity="center"
app:layout_constraintRight_toLeftOf="@id/iv"
android:layout_marginRight="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
通过上边的属性的解释,写这个布局并不难了。现在我们试试将ImageView设置为Gone再看看效果
“已设置”控件向右偏移了,我们给“已设置”控件加上app:layout_goneMarginRight=”30dp”属性再看看效果,30dp是图片的宽度。
可以看出来控件是向左偏移了一点,但是还是没有到我们第一张图的效果。我们再将30dp改成50dp再看看效果:
对比一下,这次的位置对上了。50dp是如何得来的呢?是箭头图片的宽度和“已设置”控件的marginLeft值相加得到的。
总结:layout_goneMarginLeft是指在存在控件有可见不可见状态的情况下,能够保证其约束控件的位置不会因为他的可见性而发生位置变化。到这里再想想是不是这个属性好棒啊,我们之前的写法中,为了保证不影响,只能将其设置为android:visibility=”invisible”,布局中仍然是绘制出来的,只是不可见。
居中定位
这个问题单列出来说明一下,居中是我们太常用的一个属性,但是在ConstraintLayout中会发现没有Gravity或者Center这样的属性。并不是因为我们不支持居中,只是我们用了别的形式。其实在上边的代码中已经写过了,只是没说明,来看看代码吧
主要看标记出来的位置,我们给父布局设置了固定的高度,我们需要让子view在父布局中垂直居中显示。我们在子view中给了两行代码
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
通过前边的解释,这两行代码意思就是当前view的底边缘与父布局的底部重叠,当前view的上边缘与父布局的上边缘重叠。这样怎么就居中显示了呢?我们可以这样理解,因为我们的子view没有父布局那么大,而父布局在上下给子view一个同样的约束,就相当于上下给了相同的牵引力,然后就使其居中了。左右居中的话也是一样的道理。
倾向(bias)
但是在居中的基础上,ConstraintLayout布局中多了一个倾向的属性。顾名思义,就是可以使约束倾向某一边。
- layout_constraintHorizontal_bias (0最左边 1最右边)
- layout_constraintVertical_bias (0最上边 1 最底边)
也很好理解,就想alpha值一样,从0-1,代表着倾向的力度,同时带着方向,就是倾向的方向,水平还是竖直。
比例(ratio)
继续引出比例的属性,讲了那么多属性,其实ConstraintLayout也有百分比布局的功能。这个属性就是我们可以定义view的宽高比例,想想在我们的开发中,宽高比例最不好办的应该就属banner图了,太不好适配了,ui又是像素眼,一像素的差别都能看出来。有了这个就好办了,我们可以定义banner的比例了,这样就不会变形了,啊哈哈。看代码理解吧
上图给出了代码和代码显示出来的样式。我们这只了宽度为0,高度包裹内容,比例为4:1。样式中我们看到了宽度是高度的4倍,高度就是包裹内容的高度。但是要设置比例,所有有一方肯定是变量,所以我们至少得设置一方的约束大小为0。是不是很好理解。另外我们也可以指定W和H来指定哪个维度被约束。例如app:layout_constraintDimensionRatio=”w,4:1”,就是宽是高的4倍。就是这样,总结一句话,就是有一个固定维度来通过比例约束另一个维度。
链(Chain)
说了那么多属性了,接下来要说的是类似于线性布局中的比重样式。咱们的ConstraintLayout也能很好的实现。直接看代码和结果吧
如上图,我们实现了三等分,看代码中我标记出来的位置,我们的三等分是两两之间的约束实现的。看图可能更好理解。
三个tv两两之间设置约束,最外层还一定要加上父布局的约束。这个很重要,外边不加上父布局的约束的话是没效果的,我一开始写的时候就踩了这样一个坑。这个也很好理解,外边固定约束位置,我们才能约束剩下的位置。
另外我们看到,上边的图中标出了链头,通过链头,我们可以设置一些属性layout_constraintHorizontal_chainStyle来决定这个链的展示效果。
chainStyle的三个属性值:
- spread 默认值
- packed
- spread_inside
这几个属性借助官网的图来理解吧,说实话,我也还在探索中。
GuideLine
Guideline是ConstraintLayout中的一个工具类,不会被显示,仅仅用于辅助布局。看一个例子吧
orientation指定方向,可以是横向的也可以是竖向的
决定位置的三种方式:
layout_constraintGuide_begin
layout_constraintGuide_end
layout_constraintGuide_percent
在看GuideLine的源码
源码内容不多,很好理解,super.setVisibility(8);默认是Gone。另外public void setVisibility(int visibility) {}该方法被空实现,所以我们没有办法改变他的可见度。在我们的开发使用中仅仅只是个辅助参考而已。
其他
这部分主要说说,在使用ConstraintLayout的时候遇到的一些问题
问题一:
ConstraintLayout 与ScrollView 嵌套时ScrollView 内容显示不全,且不能滑动;
解决方案:
https://stackoverflow.com/questions/40637442/bottom-of-scrollview-clipped-when-using-constraintlayout
问题二:一个checkBox的两个状态的图片大小不一致,会默认记住第一个状态的大小,第二个状态的图片只能显示出来第一个图片大小的部分
问题三:被约束控件在约束控件前边的时候需要使用@+id
总结
ConstraintLayout的内容到此介绍的差不多了,其实也并没有那么难,我的总结就是在RelativeLayout的基础上强化了,让子view之间的约束可以更好的展开,减少了布局的嵌套,但说到底也还是ViewGroup。在复杂的布局中我们还是需要嵌套。所以根据自己的需求选择最优的布局方案就好。