在2016年的Google I/O大会中,Google为Android开发者带来了新的福利——ConstraintLayout(约束布局)和layout editor(布局编辑器)。使用ConstraintLayout,能够让我们应用的布局更加“扁平化”,提升我们的开发体验。而layout editor是Android Studio 2.2新增的一个可视化布局工具,使用它可以通过拖动UI控件来进行“约束布局”,还可以实时显示ConstraintLayout的布局效果。

译者注:使用ConstraintLayout并不需要Android Studio 2.2或以上版本,但是要使用layout editor需要将Android Studio先升级到2.2或以上版本。使用ConstraintLayout需要我们添加如下依赖:

compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha4'

本篇文章会介绍如何使用ConstraintLayout的各种特性。在下篇文章中,我们会分析ConstraintLayout的工作原理,理解了工作原理,我们对它的运用也会更加得心应手。

先让我们从约束(constraint)是什么开始...

约束的类型

根据官方的介绍,一个对View的约束描述的是"View在布局中相对于其他元素的位置是怎样的"。在一个约束关系中,至少有两个主角,一个我们称之为source(源),另一个我们称之为target(目标)。其中source的位置依赖于target。我们可以这样理解:约束以某种方式将source与target连接了起来,这样source相对于target的位置便是固定的了。 我们可以将source和target看作是位于View上的点,称之为“锚点(anchor point)”。ConstraintLayout中的每个View都支持将如下锚点作为约束关系中的source或target:

  • top, bottom, left(start), right(end)

  • centerX, centerY

  • baseline

在XML文件中,是这样描述一个约束的:

layout_constraint[SourceAnchor]_[TargetAnchor]="[TargetId]"

上面的描述可能比较抽象,我们来看一个例子。这个例子建立了button_cancel的最右端(end)和button_next的最左端(start)的约束。相应的XML文件如下:

<Button   android:id="@+id/button_cancel"   … /><Button   android:id="@+id/button_next"     app:layout_constraintStart_toEndOf="@+id/button_cancel"   … />


上面的描述的约束会使得button_cancel和button_next布局在同一水平线上,并且button_next位于button_cancel的右边。

在定义一个约束时,我们需要注意为约束关系中涉及到的View指定一个id属性,ConstraintLayout通过id属性才能定位到约束关系中的target或是source。

我们来看一个完整的描述约束关系的XML文件:


<ConstraintLayout  xmlns:android="..."  xmlns:app="..."      android:id="@+id/constraintLayout"  android:layout_width="match_parent"   android:layout_height="match_parent">  <Button     android:id="@+id/button_cancel"     android:layout_width="wrap_content"     android:layout_height="wrap_content" *      app:layout_constraintStart_toStartOf="@+id/constraintLayout"    android:layout_marginStart="16dp"          app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"    android:layout_marginBottom="16dp" />   <Button     android:id="@+id/button_next"     android:layout_width="wrap_content"     android:layout_height="wrap_content"     app:layout_constraintStart_toEndOf="@+id/button_cancel"     android:layout_marginStart="16dp"     app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"    android:layout_marginBottom="16dp"/>ConstraintLayout>


在上面的例子中,建立了4个约束关系,其中button_cancel中与button_next中分别定义了两个约束关系:

  • button_cancel在父View(ConstraintLayout)的底部、靠左,这是button_cancel定义的两个约束;

  • button_next在button_cancel的右边,button_next在父View的底部,这是button_next定义的两个约束。

显示效果如下:


在上面的例子中,我们建立了Button如何与父容器对齐的约束,建立约束的语法与上面介绍的相同,只不过这里的targetId是父容器(ConstraintLayout)的id。

大家可能从上个例子中注意到了ConstraintLayout支持margin。默认情况下,建立了约束关系的两个View会紧紧挨着彼此,若我们想要它们之间保持距离,就可以定义margin。

看到这里你可能想说,ConstraintLayout不就是RelativeLayout吗?别急,我们接着往下看。

偏置约束(Biasing Constraints)

当一个View与同一轴向(水平/垂直)的2个target建立了约束,默认情况下它会被放在两个target的正中间。下面的XML文件实现了把Button在父容器中的正**:

<ConstraintLayout  xmlns:android="..."  xmlns:app="..."  android:id="@+id/constraintLayout"  android:layout_width="match_parent"  android:layout_height="match_parent">  <Button    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:id="@+id/button"    app:layout_constraintTop_toTopOf="@+id/constraintLayout"    app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"    app:layout_constraintStart_toStartOf="@+id/constraintLayout"    app:layout_constraintEnd_toEndOf="@+id/constraintLayout" />


显示效果如下:



但是有时候我们不想让Button居中显示,这时我们可以通过偏置(bias)属性来实现。请看下面的XML文件:

  xmlns:android="..."  xmlns:app="..."  android:id="@+id/constraintLayout"  android:layout_width="match_parent"  android:layout_height="match_parent">  

实现的效果如下:



上图实现的是:Button偏离父容器左边缘的距离为父容器宽度的25%,偏离父容器上边缘的距离为父容器高度的25%。实现了这个效果的就是上面XML文件中的"layout_XXX_bias"属性。肿么样,这个bias属性是不是比layout_weight强大多了?实际上,当我们不提供bias属性时,默认的水平与垂直bias都是0.5,所以Button默认会在正**。

正是通过"layout_XXX_bias"等强大属性,ConstraintLayout使得应用布局尽可能”扁平化“。实际上,所有其他布局管理器能做的,ConstraintLayout都能做到,它甚至可以同时具备好几种布局管理器的功能。

锚向指示线(Anchoring to Guidelines)

当需要一个任意位置的锚点时,我们可以使用指示线(guideline)。指示线实际上是View的子类,加入布局的方式也和普通View一样。指示线有如下特殊属性:

  • 它们的测量宽高总是0;

  • 它们的可见性总是View.GONE。

指示线的存在只是为了为其他View定义一个水平或垂直的锚点。我们来看一个例子:

<ConstraintLayout    xmlns:android="..."  xmlns:app="..."  android:id="@+id/constraintLayout"  android:layout_width="match_parent"  android:layout_height="match_parent">  <android.support.constraint.Guideline    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:id="@+id/guideline"    android:orientation="vertical"    app:layout_constraintGuide_begin="72dp" />  <Button    android:id="@+id/button_cancel"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    app:layout_constraintStart_toStartOf="@+id/guideline"    app:layout_constraintTop_toTopOf="@+id/constraintLayout"    app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"    app:layout_constraintVertical_bias="0.25" />  <Button    android:id="@+id/button_next"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    app:layout_constraintStart_toStartOf="@+id/guideline"    app:layout_constraintTop_toTopOf="@+id/constraintLayout"    app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"    app:layout_constraintVertical_bias="0.75" />ConstraintLayout>


聪明的你一定猜到了上面文件的布局效果:



在上面的文件中我们设置了一个距离父容器左边缘72dp的垂直指示线。这样一来,Button就能够使用这个指示线作为它的target。指示线可以使用以下三个属性之一:

  • layout_constraintGuide_begin:指示线距离父容器左边缘的绝对距离

  • layout_constraintGuide_end:指示线距离父容器右边缘的绝对距离

  • layout_constraintGuide_Percent:指示线距离父容器左边缘的距离,这个属性的值是一个百分比,表示距离占父容器宽度的比例

在当前的alpha版本中存在一个关于指示线的bug:在XML文件中,指示线的定义必须放在引用它的View之前,这样才能正常工作。

View的尺寸

我们已经讨论了许多关于View如何放置的问题。现在我们来讨论下关于View尺寸的问题。关于为View指定尺寸,ConstraintLayout的方式可能与你以往使用的不大一样。ConstraintLayout提供了三种方式用于指定子View的尺寸:

  • Exact: 为子View指定一个确切的尺寸。

    • 将layout_width或layou_height设为一个非零尺寸值(xx dp)即可

  • Wrap Content: 使子View的尺寸正好“包裹”子View的内容

    • 将layout_width或layout_heigth设为wrap_content即可

  • Any Size: 让子View填满父容器的剩余空间

    • 将layout_width或layout_heigth设为0dp即可

什么鬼!match_parent跑哪去了?实际上ConstrainLayout不支持match_parent,至于为什么,后文会进行解释。简单的说就是Any Size就已经实现了match_parent的功能。

我们来看一个例子:

<ConstraintLayout  xmlns:android="..."  xmlns:app="..."  android:id="@+id/constraintLayout"  android:layout_width="match_parent"  android:layout_height="match_parent">  <Button    android:id="@+id/button_cancel"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    app:layout_constraintStart_toStartOf="@+id/constraintLayout"    app:layout_constraintTop_toTopOf="@+id/constraintLayout"/>  <Button    android:id="@+id/button_next"    android:layout_width="0dp"    android:layout_height="wrap_content"    app:layout_constraintStart_toEndOf="@+id/button_cancel"    app:layout_constraintEnd_toEndOf="@+id/constraintLayout"    app:layout_constraintTop_toTopOf="@+id/constraintLayout"    app:layout_constraintBottom_toBottomOf="@+id/constraintLayout" />ConstraintLayout>


我们可以看到,button_next在指定尺寸时,使用了Any Size方式:它的layout_width被设为了0dp,这意味着它会在水平方向填满父布局的剩余可用空间。显示效果如下:


另一个牛逼的属性

一个常规的UI布局需求就是把一个View的尺寸设为特定的宽高比。对于图片来说这个需求更是常见,比如将图片的宽高比设备1:1, 4:3, 16:9等等。通过使用ConstraintLayout,我们无需再创建一个自定义View来实现这个效果,只需使用layout_constraintDimensionRatio属性即可。

使用这个属性时,我们需要固定View的宽或是高。假如我们固定了View的宽,并为其设置了一个宽高比,View的高就会在这个宽高比的约束下随着View的宽变化而变化。使用示例如下:

<ConstraintLayout  xmlns:android="..."  xmlns:app="..."  android:id="@+id/constraintLayout"  android:layout_width="match_parent"  android:layout_height="match_parent" >  <ImageView    android:layout_width="0dp"    android:layout_height="wrap_content"    android:src="@drawable/water"    app:layout_constraintDimensionRatio="16:9"    app:layout_constraintLeft_toLeftOf="@+id/constraintLayout"    app:layout_constraintTop_toTopOf="@+id/constraintLayout"    app:layout_constraintRight_toRightOf="@+id/constraintLayout"    app:layout_constraintBottom_toBottomOf="@+id/constraintLayout"    app:layout_constraintVertical_bias="0.0" />  <ImageView    android:layout_width="0dp"    android:layout_height="wrap_content"    android:src="@drawable/grass"    app:layout_constraintDimensionRatio="4:3"    app:layout_constraintLeft_toLeftOf="@+id/constraintLayout"    app:layout_constraintRight_toRightOf="@+id/constraintLayout"    app:layout_constraintBottom_toBottomOf="@+id/constraintLayout" />  . . .ConstraintLayout>


在上面的布局文件中我们分别固定了两个ImageView的高和宽,然后将另一个维度的尺寸设置为了0dp,并为两个ImageView分别指定了一个宽高比。显示效果如下:

更多相关文章

  1. 【自定义View系列】android的UI结构
  2. Android状态栏透明方法,与工具栏颜色一致
  3. Android(安卓)开发之 RecyclerView Adapter 模板
  4. 10天学通Android开发(4)-用户布局与常用控件
  5. Android(安卓)UI开发篇之 ViewPager+九宫格布局 实现左右滑动
  6. android布局优化 笔记
  7. linearlayout总结
  8. DataBinding使用指南(三):生成的binding类
  9. 指尖上的Android之实战篇(七)

随机推荐

  1. zabbix3.4安装
  2. 磁盘挂载问题:Fdisk最大只能创建2T分区的
  3. 数组,对象,传参解构; 访问器属性的get,set
  4. 第17章 0304-自己动手写个迷你小框架,学习
  5. 最全C/C++教程 你需要的全都有!
  6. 使用Oracle Stream Analytics 21步搭建大
  7. 共同期待美好2020
  8. ES6 模块知识入门
  9. Java并发编程学习4-线程封闭和安全发布
  10. ISIS路由泄露,如何避免路由环路?