ConstraintLayout 使用手册

背景

之前我们在这边文章中 Android 优化之布局优化 了解到可以通过使用 ConstraintLayout 来构建我们的布局,这也是 Android 官方推荐首要使用的,手动拖拽的方式习惯后也大大提高了我们的开发效率,如果你还没了解过 ConstraintLayout ,那就继续往下看吧。如果你已经熟练使用的话,不妨扫一眼,说不定有意外的收获。

添加约束条件

1.常规约束

创建约束条件时,每个视图都必须有两个约束条件:一个水平约束条件,一个垂直约束条件,如果我们什么约束条件都没有添加,控件就会位于ConstraintLayout 的左上角。添加约束条件非常简单,我们可以选择手动拖拽的方式或者直接手动编码的方式,个人喜欢拖拽的方式,如果有误差再在布局文件中进行微调。下面示例为 TextView 添加了上下左右四个约束条件:


可以看到,我们在拖动的过程中,布局文件也会生成相应的代码。上图演示的是链接到父布局,除此之外,我们也可以链接到其他控件中,这里不再做演示,以下是常用的约束条件:

  • 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

这些约束条件应该都可以顾名思义,一个比较特别的是 layout_constraintBaseline_toBaselineOf,它用于将一个视图的文本基线与另一视图的文本基线对齐,要创建基线约束条件,可以右键点击要约束的文本视图,然后点击 show Baseline,接着点击文本基线并将其拖到另一基线上。

有些同学可能会对 start 和 left、end 和 right 有困惑。其实如果应用只是面向国内市场的话, start 等价于 left,end 等价于 right ,因为中文的书写方向是从左到右的,但是有些语言是从右到左的书写方式,典型的就是阿拉伯语,所以 Android 从 4.2 开始推荐使用 start 、end 来代替 left 、 right,这样在切换到 RTL 语言时,UI 会自动进行镜像翻转,可以保持一致的用户体验。

2.Guideline 约束

我们可以添加垂直或水平的 Guideline 来约束视图,相当于辅助线一样,用户是看不到 Guideline 的。

通过请点击工具栏中的 Guideline


然后点击 Add Vertical Guideline 或 Add Horizontal Guideline,拖动虚线将其重新定位,然后点击引导线边缘的圆圈以切换测量模式,有 固定数值百分比 两种模式。

3.Barrier 约束

与 Guileline 类似,Barrier 是一条隐藏的线,Barrier 的位置是根据其所包含的视图的位置而移动,包含视图的属性是 constraint_referenced_ids,Barrier 可以是垂直或水平的,可以创建到引用视图的顶部、底部、左侧和右侧。以下示例,Barrier 包含了 id 为 tv_1,tv_2 的 TextView,而 id 为 tv_3 的 TextView 在 Barrier 的右侧。

代码:





    

    

    


    

效果:


调整视图尺寸

我们一般是通过 layout_width 和 layout_height 来为视图指定尺寸,可供的选择有 match_parent、wrap_content 和 具体的数值,ContraintLayout 亦如此,不过它多了一个选择叫作 Match Constraints,在代码中的体现是 0dp,除了可以在代码中直接设置外,我们还可以在编辑器右侧的 Attributes 窗口,点击相应的位置更改,以宽度为例,操作如下:

从动图中可以看到,当我们切换到


layout_width 变为 0dp 了,代表当前的模式为 match_contrains,此时视图是撑满的,这和 layout_constraintWidth_default 的值有关:

  • spread : 尽可能扩展视图以满足每侧的约束条件,默认值。
  • wrap : 仅在需要时扩展视图以适应其内容。这个与在 layout_width 设置为 wrap_content 的区别是 wrap 会受到约束条件的限制,即约束条件优先。而设置 layout_content 为 wrap_content 会强行使宽度始终与内容宽度完全匹配,即内容优先。
  • percent : 顾名思义,设置为百分比的形式,在设置了这个值之后,我们就可以通过 layout_constraintWidth_percent 指定百分比的具体数值(范围为 0 到 1),当然如果指定 layout_constraintWidth_default 为 spread ,设置 layout_constraintWidth_percent 属性也会生效。

调整约束偏差

当我们对某个视图两侧添加约束条件(并且同一维度的视图尺寸为 fixed 或者 wrap_cotent)时,该视图在两个约束条件之间居中,默认偏差为 0.5,对应的属性是 layout_constraintVertical_biaslayout_constraintHorizontal_bias,可以进行对其调整满足业务需求:

将尺寸设置为比例

如果视图至少有一个尺寸设置为 match_constraints(0dp),我么就可以把视图设置为比例的形式,对应的属性是 layout_constraintDimensionRatio ,如下,我们设置了宽充满屏幕,比例为 1:1 的 TextView:

当然,我们可以把宽高都设置为 match_constraints(0dp),这种情况下视图会先满足约束条件,然后把视图指定为该比例的最大尺寸。我们也可以在比例前面加一个 W,或者 H,来约束宽和高,如下:

       
    
       
    

边距(Margin)

ContraintLayout 的边距只有在有约束条件的情况下才会生效,比如下面这段代码中TextView 没有添加任何约束条件,最后它会显示 ConstraintLayout 的左上角,设置的 margin 不会生效。



    

需要注意的是,ConstraintLayout 的边距设置为负值并不会生效,这点和其他传统布局是有区别的。

此外,ContraintLayoutMargin 还提供了 GONE_MARGIN:

  • layout_goneMarginStart
  • layout_goneMarginEnd
  • layout_goneMarginLeft
  • layout_goneMarginTop
  • layout_goneMarginRight
  • layout_goneMarginBottom

当约束目标被设置为 View.GONE 后,设置的 GONE_MARGIN 就会生效。

圆形(角度)定位(Circular positioning)

我们可以通过一个角度和距离来约束两个视图的位置,引用官方的一张图:


对应的属性是 :

  • layout_constraintCircle ,参照视图的 id
  • layout_constraintCircleRadius ,该视图中心与参照视图中心的距离
  • layout_constraintCircleAngle ,该视图位于参照视图的角度(0° ~ 360° )·


    
    
   
    
    

上面实现的效果如下:


Chain,链控制

链是控制一组视图的,视图可以是水平链和垂直链的一部分,但是使用链并不会使链中的视图对齐在同一方向上,因此,我们要指定额外的约束条件。以下示例是创建一个水平链并以视图的顶端对齐:


创建链之后,会有一个 "链头"(Chain Head),链头是链中的第一个元素(水平链中最左侧的视图,垂直链中最顶部的视图)。链最重要是它的样式,我们可以通过选择链中的元素,右键点击 Cycle Chain mode 进行样式切换,当然也可以在链头里设置 layout_constraintHorizontal_chainStyle , 链的样式取值有以下几种(不会忽略 margin 的取值):

  • spread,视图是均匀分布的。
  • spread inside,第一个和最后一个视图固定在链两端的约束边界上,其余视图均匀分布。
  • packed,链内视图被打包一起。

当链的样式设置为 spread 或者 spread inside 时,且我们把一个或多个视图设置为 match_constraints(0dp)。默认情况下,设置了 match_constraints 的属性会把剩余空间均匀分配,但是我们可以使用 layout_constraintHorizontal_weight 和 ayout_constraintVertical_weight 属性来分配权重。这和 LinearLayout 的 layout_weight 的原理是一样的。这种方式也叫做 weight chain

另外,当链设置为 packed 的样式之后,我们可以通过链头的视图偏差 layout_constraintHorizontal_bias 属性来调整整条链的偏差。这种方式称作 packed chain with bias

下面这张来自官方的图可以帮助我们理解链的不同样式之间的区别:


Group

Group 可以把多个控件归为一组,方便隐藏或显示一组控件,相比我们在外面包一层 ViewGroup 的方法,性能上有优势。使用方式如下:

    

不过需要注意的是如果控件被包含在 Group 中,单独一个控件设置 View.GONE 并不会使自己隐藏掉(在 Group 为 View.VISIBLE 的情况下),这是因为设置 View.GONE 会导致重绘,调用 Group 的 updatePreLayout 方法,具体逻辑如下:

    public void updatePreLayout(ConstraintLayout container) {
        int visibility = this.getVisibility();
        float elevation = 0.0F;
        if (VERSION.SDK_INT >= 21) {
            elevation = this.getElevation();
        }

        for(int i = 0; i < this.mCount; ++i) {
            int id = this.mIds[i];
            View view = container.getViewById(id);
            if (view != null) {
                view.setVisibility(visibility);
                if (elevation > 0.0F && VERSION.SDK_INT >= 21) {
                    view.setElevation(elevation);
                }
            }
        }
    }

可以看出,updatePreLayout 方法会把 Group 内的视图的可见性设置为和 Group 的一样。

总结

随着官方的不断完善和优化,与刚出来的时候相比,ConstraintLayout 无论是使用上还是性能上都有了很大的提升,如果还没在项目中使用的同学,是时候上车了。

参考

  • 使用 ConstraintLayout 构建自适应界面
  • ConstraintLayout

你可能感兴趣的:(ConstraintLayout 使用手册)