Android ConstraintLayout使用攻略

原文链接 Android ConstraintLayout使用攻略

ConstraintLayout是新一代的布局,它汲取了众家之长,把布局的概念进行了大统一,灵活且强大,基本上可以干掉以前所有的常用布局(LinearLayout, RelativeLayout和FrameLayout)。自从Android Studio 2.3(大约在2017年)后,它就变成了Android Studio的默认模板的布局控件,可以看出谷歌对它的钟爱程度。今天就来学习一下如何使用这个新布局神器。

简述

ConstraintLayout与RelativeLayout有些类似,是一个布局管理器(ViewGroup),但要强大许多,它可以以各种方式排列子View,以及按比例伸缩。最重要的改变就是它对于『Drag and drop』拖拽式制作GUI页面支持的相当的好。当然了这个取决于个人喜好,很多人仍然喜欢用写代码的方式直接去写xml文件,包括我在内。拖拽式虽然直观,但是不方便精准控制,对于一般性的布局来说尚可,但稍复杂了后,以及有了一些可滑动的view时,就不是那么的方便了。对于喜欢拖拽的同学可以查看官方的一个教程,以及郭大婶的一篇文章,这两篇专注于拖拽式,且讲的都比较详细。

添加依赖

因为ConstraintLayout并不是在标准的SDK中,而是被放在了support SDK中,现在统一叫androidx了,所以要单独添加依赖:

     dependencies {
          implementation 'com.android.support.constraint:constraint-layout:2.1.0'
     }

概念与术语

ContraintLayout中把一切有关布局的参数都称之为Constraint(约束),长和宽,对齐,居中,margin和padding都是constraint。布局中的属性均以"layout_constraint"为前缀。
约束(Constrain)的意思是指用另外一个View(包括父布局即ContraintLayout)对当前View的某一布局参数施加影响。具体的影响叫做Constraint,另外一个View称作约束对象(Constraining Object),当前View称作被约束对象(Constrainted Object)。

在这里插入图片描述

     <Button android:id="@+id/buttonA" ... />
     <Button android:id="@+id/buttonB" ...
          app:layout_constraintLeft_toRightOf="@id/buttonA" />

排列方式

对子View的排列方式是一个ViewGroup的最基础的功能,它也体现了不同的布局管理器的作用,如线性布局(LinearLayout)是以水平或者垂直方向平铺方式来排列子View的。ConstraintLayout是以类似RelativeLayout的方式,需要针对每个子View指定如何排列。

在这里插入图片描述

基础排列方式

最为基础的排列方式就是针对每个子View,指定它相对于另外一个View或者父布局(也就是ConstraintLayout本身)的相对位置,从而确定该View的具体方位。具体就是[left, top, right, bottom]四个关键的排列元素相对于另外一个View或者父布局的位置关系。

如,layout_constraintLeft_toLeftOf=“parent”,这就是左边与父布局左边对齐;layout_constraintTop_toBottomOf=“id/header”,这是把这个View放在id为header的下面。以此类推,因为与RelativeLayout的布局参数比较类似,就不细说了,详情可参阅文档。

还有一个非常实用的Constraint叫做baseline,它是专门针对TextView的,baseline也即文本的基线,可以简单理解为文字的底部,当有两个TextiView不一样大,文字大小也不一样时,却需要对齐文本,这个属于就相当的有用。

在这里插入图片描述

    <TextView android:id="@+id/TextView1"/>

    <TextView
        android:id="@+id/TextView2"
        app:layout_constraintLeft_toRightOf="@+id/TextView1"
        app:layout_constraintBaseline_toBaselineOf="@+id/TextView1"/>

margin

常规使用与其他布局是一样的,通过layout_margin[Start,End,Left,Right,Bottom,Top]来指定与约束对象之间的margin,这个不细说了。

需要说一下,ConstraintLayout有一个goneMargin,可以用于当一个约束对象的Visibility被设置为GONE时,使用。用layout_goneMargin[Start,End,Left,Top,Right,Bottom]来设置。

比如A约束B,B在A的右边,它俩挨着,但如果A的Visibility设置为GONE时,正常情况下B就会挨到原来A的左边了,跑到了左边界上,这时可能就会变得比较丑了,如果使用margin,比如在A和B中间加一个margin,可以解决问题,但是也会影响当A可见的时候。而用goneMargin就可以完美的解决此种场景。layout_goneMarginStart=“10dip”,那么这个margin只有当约束对象A的Visibility被置为GONE时,才会生效,这时B虽然跑到了左边界上,但是还有margin,就不会那么丑了。(其实goneMargin应用的场景也比较有限,前面说的case,也可以用A和B的父布局的leftPadding来解决)

在这里插入图片描述

相当骚气的环状排列方式

除了常规的行列式排列以外,这货还非常骚气的可以环状排列,以约束对象为圆心,通过角度和半径来约束位置:

  • layout_constraintCircle 用以指定作为圆心的约束对象(其他view的id)
  • layout_constraintCircleRadius 被约束对象与圆心的距离
  • layout_constraintCircleAngle 被约束对象与横轴的角度(0~360度之间)

在这里插入图片描述

  <Button android:id="@+id/buttonA" ... />
  <Button android:id="@+id/buttonB" ...
      app:layout_constraintCircle="@+id/buttonA"
      app:layout_constraintCircleRadius="100dp"
      app:layout_constraintCircleAngle="45" />
环状排列实例

环状排列虽然骚气,但是现实中似乎应用场景不多。

居中与对齐

对齐不是大问题,前面讲的如何排列其实就是对齐,选定一个约束对象后,其他对象都受其约束,就自然对齐了。

比较常见的问题,以及大部分时候比较麻烦的是居中,平衡与中庸中符合绝大多数审美的,因此布局时,绝大多数情况下都是需要居中的。居中的实现的方式就是两边都约束于父布局(也即ConstraintLayout),如:

在这里插入图片描述

         <androidx.constraintlayout.widget.ConstraintLayout ...>
             <Button android:id="@+id/button" ...
                 app:layout_constraintLeft_toLeftOf="parent"
                 app:layout_constraintRight_toRightOf="parent"/>
         </>

居中,其实就是两边的约束边距各占空余空间的50%,扩展开来,想要实现不完全居中,两边边距呈一定比例关系,也是可以办到的。比如说黄金比例0.618就比居中好看,这也好办:
在这里插入图片描述

       <androidx.constraintlayout.widget.ConstraintLayout ...>
             <Button android:id="@+id/button" ...
                 app:layout_constraintHorizontal_bias="0.382"
                 app:layout_constraintLeft_toLeftOf="parent"
                 app:layout_constraintRight_toRightOf="parent"/>
      </>

这个比例控制叫bias,可以有Horizontal和Vertical两个方向。

尺寸

尺寸也就是针对子View的宽与高的约束,其实大部分时候一些具体的子View的宽与高要么指定大小,要么是WRAP_CONTENT的,但有些时候可能就是需要更加的灵活一些,这时就可以考虑用ConstraintLayout里面的一些特性。宽与高设置为固定大小或者WRAP_CONTENT时与其他ViewGroup是一样的,不用多说,要想特别一点的就是设置为『0dip』或者MATCH_CONSTRAINT时,就会用其他约束来决定该View的宽或者高。后面重点讨论有约束的情况。

默认行为

如果子View的宽或者高设置为了MATCH_CONSTRAINT(或者『0dip』)时,默认的行为是它会占满剩余的可用空间。

Max与Min

还可以加上最大最小的限制:

  • layout_constraintWidth_min and layout_constraintHeight_min : will set the minimum size for this dimension
  • layout_constraintWidth_max and layout_constraintHeight_max : will set the maximum size for this dimension
  • layout_constraintWidth_percent and layout_constraintHeight_percent : will set the size of this dimension as a percentage of the parent
按约束对象的比例来设置(Percent)

前面的默认行为或者最大最小还算不上啥,其他ViewGroup也有类似参数。最为变态与强大的是可以按约束对象的比例来作为此View的宽或者高:

  • The dimension should be set to MATCH_CONSTRAINT (0dp)
  • The default should be set to percent app:layout_constraintWidth_default=“percent” or app:layout_constraintHeight_default=“percent”
  • Then set the layout_constraintWidth_percent or layout_constraintHeight_percent attributes to a value between 0 and 1
自身宽高比(Ratio)

这个是最变态的约束方式,可以设置一个自身的宽高比,以确定子View的尺寸,当然了具体的宽或者高还要以其他约束方式确定具体尺寸,然后再按照设置的宽高比对另外一个进行约束。比如,实现一个方形的按扭,宽是其自身要求的宽度值(WRAP_CONTENT),设置的宽高比是1:1,所以高度也会跟宽度一样,就是一个方形的按扭了:

        <Button android:layout_width="wrap_content"
                   android:layout_height="0dp"
                   app:layout_constraintDimensionRatio="1:1" />

高级特性

前面讲的是一些基础使用方式,但是这货远不止这些,还有一些非常强大的功能,下面简单介绍两个。

链(Chains)

在某一个方向上(横着或者竖着)有着相互约束的一组子View,会被视为一个链,第一个称作头部(Head),可以应用一些样式以对整个链内的子View都产生影响。
在这里插入图片描述

这里的相互约束的意思是,比如有上面A,B,C三个子View,那么它们要相互约束,也即:

  <ConstraintLayout>
      <A layout_constraintLeft_toLeftOf="parent"
           layout_constraintRight_toRightOf="B" />
      <B layout_constraintLeft_toLeftOf="A"
           layout_constraintRight_toRightOf="C" />
      <C layout_constraintLeft_toLeftOf="B"
           layout_constraintRight_toRightOf="parent" />
  </ConstraintLayout>

就可以,对头部子View A进行样式(Chain style),通过layout_constraintHorizontal_chainStyle来设置:

  • CHAIN_SPREAD – the elements will be spread out (default style)
  • Weighted chain – in CHAIN_SPREAD mode, if some widgets are set to MATCH_CONSTRAINT, they will split the available space
  • CHAIN_SPREAD_INSIDE – similar, but the endpoints of the chain will not be spread out
  • CHAIN_PACKED – the elements of the chain will be packed together. The horizontal or vertical bias attribute of the child will then affect the positioning of the packed elements

在这里插入图片描述

链中的权重(Weighted chains)

默认情况下,子View会均分并占满可用的空间。可以用权重来按比例分配,给子View加上layout_constraintHorizontal_weight后,就会按比例分配,这个与LinearLayout的layoutWeight用法是一样的。

组(Groups)

为了View的渲染性能,各路大神告诉我们要尽可能的让布局扁平化,但是,如果太扁平了,全都放在一个ViewGroup下面,就会混乱,特别是像RelativeLayout和ConstraintLayout,子View的排列方式会产生相互依赖,会有牵一发动全身的情况出现。为了避免这种情况,就需要对子View进行分组,对页面进行区域划分,把紧密相关的视为一个组。以往,会用一个子ViewGroup把一个组包起来,虽然会加深View的层次,但这样能避免牵一发动全身。

而对于ConstraintLayout来说,有更先进的方式了,它有一个类叫Group,就是专门用来干这件事儿的,但Group对象并不是一个真的子View,这里的意思是它并不会在View tree中进行渲染,它是专门用于管理属于它的子View的,比如方便对整个组进行Visibility的设置。

神器要如何使用

前面的介绍就差不多了,ConstraintLayout还是相当的强大的,如有可能还是尽可能的多用它吧。它的实现上面确实挺复杂的,毕竟功能比较强大,但它的效率并不差。对于常用的几大布局都可以直接用它来替代。

当线性布局使用(as LinearLayout)

线性布局最大的优势就在于可以用weight的方式来按比例排放,而这个用前面提到的Chain就可以完美的解决。所以,LinearLayout可以完全放弃。

当层叠布局使用(as FrameLayout)

FrameLayout的全用场景一般是作为整个应用的根布局,特别是HomeActivity+Fragment这种架构。从纯的功能角度来讲,ConstraintLayout可以完全实现FrameLayout的所有功能,所以,FrameLayout也可以放弃。

但从简单方便角度来讲,假如是HomeActivity的根布局,子View都是MATCH_PARENT的Fragement的话,也没有必要换成ConstraintLayout,这种场景FrameLayout完全够用,而且非常适合它。换成ConstraintLayout反倒有些浪费,有些杀鸡用牛刀。

当相对布局使用(as RelativeLayout)

从前面的讲述可以看出,ConstraintLayout几乎就是RelativeLayout的加强版。所以,凡是用到RelativeLayout的地方都应该换成ConstaintLayout

参考资料

原创不易,打赏点赞在看收藏分享 总要有一个吧

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值