翻译By Leelion6。关于 ConstraintLayout 的文章其实已经不少了,不过看到这篇文章写的很有趣,以及在翻译的过程中,感受到了不同文化环境下,写作思维的不同。最关键的是,这篇文章的内容很细致,对初学者比较友好,所以翻译过来以供需要的开发者去学习。如果需要进阶的用法欢迎去看郭霖等大神更深入一些的相关文章。

在本教程中,你将会使用ConstraintLayout从头开始构建一个登录界面,从中学习创建Android 视图相关的基础知识。

作者:Fuad Kamal

原文链接:https://www.raywenderlich.com/9193-constraintlayout-tutorial-for-android-getting-started

所需材料及源码下载:https://koenig-media.raywenderlich.com/uploads/2019/01/RazeGalactic-1.zip

发布日期:2019年1月30日 <br><br> 一款优秀的Android应用程序,需要的不仅仅是美观的UI界面,同时还要有良好的性能表现。在绘制页面的时候,你需要把控视图在用户屏幕上的何处出现,以及它出现的方式

Android提供了多种布局类型,这些布局中确定子View的方式都是不同的。所有布局都来自ViewGroup类。 <br><br> 构建Android UI常用的布局有FrameLayout, LinearLayoutRelativeLayout.

这些布局易于使用,但当视图结构变得复杂时,它们每个都有其局限性和一些性能问题:

<br> 将包含 layout_weight属性的 LinearLayoutRelativeLayout一起嵌套使用,将会指数级地增加布局性能成本。这时就需要 ConstraintLayout 约束布局来进行拯救了。

在较新版本的Android Studio中,ConstraintLayout已经作为默认布局存在了,并且提供了许多放置对象的方法:可以相对于它们的容器本身进行约束、相对于其他对象约束或者彼此之间进行约束、以及相对于你自己创建的guileline(辅助线)进行约束。这些方法可以让你在同一层次中创建大型、复杂、动态和响应式的视图,甚至还能支持动画! <br><br>

Raze Galactic - 一款星际旅行应用程序

您可以使用该应用程序预定行星之间的旅行,计划周末在空间站的度假,并在您抵达后预定月球车。

在本教程中,你将通过从头开始构建星际旅行应用程序的登录界面UI,以了解ConstraintLayout的基本功能。

在此过程中,你将学习:

注意:本教程假设你熟悉 Android 和 Android Studio 的基础知识。如果你不熟悉 Android 开发,请先查看我们的 Android入门教程 系列。

<br> <br>

入门

打开Android Studio 3.2.1或更高版本,通过从启动界面选择 Start a new Android Studio project 来创建新的Android Studio项目。

在下一个界面中,输入 Raze Galactic 作为 Application name (应用程序名称)。

对于 Company domain (公司域名),你可以输入任意你喜欢的内容。该示例使用 raywenderlich.com

对于 Project location (项目位置),请选择计算机上对你来说有意义的地址,并确保地址路径中没有空格。你可以点击项目位置字段右侧的省略号 ... 以直接选择计算机上的目录。

最后,确保已选中 Include Kotlin support (添加Kotlin语言支持) 并点击 Next (下一步) 按钮。

译者注:勾选Kotlin语言支持在本文章中并非必要,后续的内容没有直接使用到Kotilin,所以如果你是纯粹的JAVA语言开发者或者不想使用Kotlin,这里不勾选也完全OK。

在接下来的界面中,进行Android设备选择,你需要选择 Phone and Tablet (手机和平板电脑),然后选择 API 28: Android 9.0 (Pie) 并点击Next。

译者注:API部分可以按自己习惯选择,不一定非要到Android9.0这么高的版本。

在“Add an Activity to Mobile” (为移动设备添加活动) 界面,选择 Empty Activity (空活动)。在之后你将向这个空活动中添加UI元素,以了解Android中的布局操作。

接下来———你猜对了———点击Next!

在最后一个向导页,Configure Activity(配置活动),保留所有默认配置,需要按Finish即可完成项目配置。

<br><br>

检查ConstraintLayout版本

在开始创建新布局之前,你需要检查ConstraintLayout是否需要版本更新。

你可以在app模块的构建Gradle文件中找到此信息。在Andoird Studio中,打开 app ▸ build.gradle 并查看其中的 dependencies { … }部分。

在各种构建依赖项中,你应该能找到 implementation 'com.android.support.constraint:constraint-layout:x',其中x是项目中ConstraintLayout所使用的版本。

要完成本教程,你需要ConstraintLayout 1.1.3或更高版本。为了确保你拥有最新版本的ConstraintLayout库,请从菜单中选择 Tools ▸ SDK Manager

单击 SDK Tools (SDK工具) 选项卡,在 Support Repository (支持的库) 下查看 ConstraintLayout for Android ,如果显示 installed (已安装),则表示你使用的是最新版本

接下来,选中名为 Show Package Details (显示包详情) 的复选框,以查看项目中包含的库的版本,Gradle文件中的版本号需要与 SDK manager (SDK 管理器) 中安装的版本相匹配。

<br><br>

通过配置 Android Studio 的设计界面以实现高效的约束布局开发

在继续学习本教程之前,请设置Android Studio视图显示方式,以便更轻松地添加和查看约束以及其相关元素。

首先打开 activity_main.xml ,然后单击工具栏中的 Select Design Surface (选择设计界面) 图标,并选择 Design + Blueprint (设计+蓝图)。

现在你在工作时就能看到设计预览界面旁边的蓝图界面了。

蓝图视图可以帮助你更清晰地查看约束和 guidelines (辅助线),而不会被内容或背景分散注意力。

在开发 APP UI 时,你可以在代码视图 (Text tab) 和设计视图 (Design tab) 之间来回切换。

代码视图允许你查看和编辑布局背后的XML,而设计视图对于可视化操作UI元素非常有用。设计视图还提供了 Component Tree (组件树) , 它能让你查看和选择节目中存在的所有UI元素。

当你处于代码视图中,能够看到视觉预览和蓝图是很有用的。这样,当你在预览界面中选择任何元素时,XML中相应的代码块将变成高亮显示。

如果你在代码视图中没有看到预览界面,请单击右侧的 Preview (预览) 选项卡。

<br><br>

从头开始创建新的ConstraintLayout

对于接下来的这一步,请打开 activity_main.xml 并切换到代码视图以查看该文件的源代码:

<?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"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

请注意,此布局的默认根元素是android.support.constraint.ConstraintLayout。另外,带有 “Hello World!” 文本的TextView元素已经有了一些约束属性,例如app:layout_constraintBottom_toBottomOf="parent",它的作用是将此视图的底部约束到其父容器的底部。

在相对其父容器顶部、底部、左侧还有右侧都约束后,包含 “Hello World!”的 TextView便处于了屏幕的中心。

切换到设计视图并将鼠标光标移到包含 “Hello World!” 的TextView上, 可以看到有四条波浪线将TextView连接到其父容器上,这些线表示TextView已经应用了约束规则。

如果没有出现这些线,请单击TextView,然后它们就会出现。如果已选中 Show Constraints (显示约束) 视图选项,则无需使用鼠标光标悬停或选择任何视图,就可以看到所有约束。

<br><br>

向APP中添加图像

对于下一步,你将需要在本教程开头的应用程序屏幕截图中看到的火箭图像。使用教程开头的“ 下载材质”按钮下载本教程的材料,或者点此下载。你可以在 RazeGalactic-starter 文件夹中找到 track_icon.png

获取 track_icon.png 并将其添加到项目的 drawable 文件夹中。您可以通过在Mac上使用 command-C 或在Windows上使用 control-C 进行复制,然后右键单击Android项目中的 drawable 文件夹并使用Mac上的 command-V 或Windows上的 control-V 粘贴图像到项目中。

您可以将UI元素从 Palette (调色板) 拖放到设计界面中。如果没有看到 Palette ,请单击垂直 Palette 选项卡图标。

删除“Hello World!”,然后将以下UI元素拖到 Palette 中的视图中:

更改UI元素的文本并将其拖动到屏幕周围,以使它们看起来像本教程开头的最终布局预览。不要过分在意视图的尺寸、间距或对齐的精度。

接下来,你需要添加android:textAppearance="@style/TextAppearance.AppCompat.Headline"Raze Galactic TextView以获得合适的样式。

请注意,当你在屏幕上拖动视图时,这些小对齐线是能帮助到你的,这些线条可以很容易地将视图们相互排列。

<br><br>

测试视图放置的位置

现在,构建并运行你的应用程序。

天了噜!视图没有按你在设计视图中分配好的位置排列!一切都在屏幕的左上角蜷缩着,这是肿么回事?

嗯..好吧,Android得到如何放置UI元素的这部分信息,因为你添加的视图没有任何已定义的约束。所以你需要解决这个问题。

回到Android Studio,可以看到 Component Tree (组件树) 中的每个新视图现在都有一系列错误。要查看错误的完整文本,请单击Component Tree (组件树)中的红色感叹号。

错误如下:

此视图不受约束。它仅包含在刚添加时的位置,所以它会在运行时放置于(0,0)的位置,除非你添加约束。<br/> 切换回代码视图以检查布局XML的源代码。请注意,任何视图的源代码都有一些带tools前缀的属性,例如:

<ImageView
    android:id="@+id/imageView"
    android:layout_width="46dp"
    android:layout_height="46dp"
    app:srcCompat="@drawable/track_icon"
    tools:layout_editor_absoluteX="16dp"
    tools:layout_editor_absoluteY="16dp" />

布局编辑器允许你将小部件放在画布上的任何位置,并使用tools前缀属性记录当前位置。这些属性在实际运行时并不会被应用,请记住这一点。

译者补充: tools前缀的属性都是服务于布局编辑器中的,只在XML的预览视图中起作用,实际运行时是没有用的。 <br><br>

开始了解布局编辑工具栏

切换回布局的设计视图,你可以在布局预览得上方看到一堆小控件。如果将鼠标悬停在每个控件上,可以阅读该控件的简要说明。 <br><br>

自动连接

单击工具栏中的磁铁图标以启用 Autoconnect (自动连接) ,将任何视图拖动到父视图角落附近的区域,此时Android Studio将自动为你创建新约束。

请注意,这仅适用于在UI元素及其父视图之间创建约束,而不是在UI元素之间创建约束,因此 Autoconnect 的用处非常有限。大多数情况下,你还是关闭 Autoconnect 吧。 <br><br>

自动连接(推测约束)

接下来,单击 Infer Constraints (推测约束) 按钮,Android Studio会自动添加布局中缺少的约束。

再次构建并运行您的应用,会发现现在这些组件不在角落,而是出现在你放置它们的位置了。

这是添加约束的最简单方法,但也可能是最不好的。因为Android Studio布局的意图往往和你实际的意图不同。

使用自动连接在特定大小的情况下布局可能是正常的,但在不同的屏幕大小或屏幕方向上,可能就不会像你所希望的样子布局了。

<br><br>

清除所有约束

如果约束混乱,你可以清除所有约束并从头开始重新添加约束。具体方法是,点击 Clear All Constraints (清楚所有约束) 按钮清除当前页面所有约束。

<br><br>

添加和删除个别约束

Android提供了多种选项,可以将UI元素限制在屏幕的不同部分,让你灵活地设计布局。

在本教程的这一部分中,你将学习如何将对象约束到其容器,删除单个约束以及将对象约束到另一个对象以达到动态布局的效果。 <br><br>

将对象约束到其容器

请注意,当你单击ImageView时,它会突出显示,并且视图的顶部,底部,左侧和右侧会出现小圆圈。这些圆圈是你的约束锚点。

当你将鼠标悬停在其中一个约束锚点上时,它将闪烁绿色。Android Studio会提示你创建约束连接。

单击元素左侧的圆圈并将其拖动到左侧。在拖动时,将出现带箭头的线条,在UI元素的左侧和要将其连接到的元素之间创建约束。

将圆拖动到视图的左侧,并将圆圈ImageView的 左侧约束到父视图的左部。对圆圈ImageView的顶部重复此操作,将其约束到父视图顶部。您现在已将ImageView的约束限制在视图的左上角。

将图像向下拖动一点,Android Studio将在顶部创建一个边距。通过在代码视图中编辑XML代码或在设计视图的 Attributes (属性) 检查器中对其进行编辑,将顶部边距设置为30dp。

接下来,选择 Raze Galactic TextView, 将左约束锚点拖动到父视图的左侧,将右约束锚点拖动到父视图的右侧。通过将此UI元素约束到左侧和右侧,Android明白你是想将其达到居中的效果。

<br><br>

删除个别约束

接下来,将鼠标光标放在已设置约束的约束锚点之一上,表示箭头的圆圈现在应该闪烁红色,约束也会以红色突出显示。

单击锚点就会删除约束。现在先不要单击,并将约束锚点保留在原位,但如果以后需要删除约束,请记住该操作。

现在你知道如何将UI元素约束到其父容器的边框,接下来到了学习如何将UI元素相互约束的时候了。 <br><br>

将对象彼此之间进行约束

在本教程的这一步中,你将实现 Raze Galactic TextView始终与火箭图像对齐。

要做到这一点,你需要把 Raze Galactic TextView的顶部锚点约束到ImageView的顶部锚点,同时将TextView底部锚点固定到ImageView的底部锚点。两个视图便会垂直对齐。

现在,如果你按住火箭上下拖动,你将看到 Raze Galactic 随着图像上下移动。

稍后,你将看到如何使用对齐菜单创建这样的对齐。但是,该方法并不总是完美无缺的,所以知道如何手动完成它也是一件好事。

切换到Android Studio中的代码视图,并检查刚才添加约束的视图的代码:

  <ImageView
    android:id="@+id/imageView2"
    android:layout_width="46dp"
    android:layout_height="46dp"
    android:layout_marginStart="16dp"
    android:layout_marginTop="30dp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:srcCompat="@drawable/track_icon"/>

  <TextView
    android:id="@+id/textView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginEnd="8dp"
    android:layout_marginStart="8dp"
    android:text="Raze Galactic"
    android:textAppearance="@style/TextAppearance.AppCompat.Headline"
    app:layout_constraintBottom_toBottomOf="@+id/imageView2"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@+id/imageView2"/>

现在你已经添加了一些约束,带有tools前缀的属性已经消失,因为Android Studio现在不再需要仅设计时生效的这些属性了。

你添加的每个约束都会在代码中提现,比如app:layout_constraintTop_toTopOf="parent",代表该元素的顶部约束到了父布局的顶部。

你可以看到Android Studio为你设置了一些边距,也许你并没有想过去添加。这些边距可能看起来像android:layout_marginStart="16dp"

继续往下看,在 Raze Galactic TextView中 删除边距属性后切换回设计视图,现在你会发现, Raze Galactic TextView应该与火箭图像对齐了。

这两个视图不会再有错误出现了,因为已经满足了Android Studio放置这两个UI元素所需要的信息量下限。

注意:你仍可能会看到有关使用硬编码字符串或丢失的警告contentDescription。不过你现在现在可以忽略这些。

现在,你应该将 ** Login** (登录)按钮与 Sign Up (注册) 按钮对齐,就像将 Raze Galactic TextView与火箭图像对齐一样。为此,你将需要设置三个约束:

“登录”按钮的顶部锚点位于“注册”按钮的顶部。 “登录”按钮的底部锚点位于“注册”按钮的底部。 “登录”按钮的左侧锚点位于“注册”按钮的右部,并设置从左起30dp的起始边距以在它们之间留出一些空间。

<br><br>

应用对齐约束

选择 Raze Galactic TextView并单击右侧锚点以删除这些约束。然后,在TextView仍然选中时,单击工具栏中的 Align (对齐)工具,然后在选择 Horizontally in Parent (水平布局)。

这会自动将Raze Galactic TextView相对父容器进行居中布局。这与你在手动添加约束时之前实现的效果相同。

事实上,如果你切换到代码视图并仔细检查Raze Galactic TextView,你会注意到Android Studio添加了以下约束属性:

app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"

这时你可能问了,Android Studio中自动添加的layout_constraintHorizontal_bias是什么? <br><br>

Constraint Bias(约束偏差)

当视图在水平或垂直方向上受到约束时,无论是父视图还是其他视图,默认情况下它具有0.5或50%的约束偏差。换句话说,视图保持在它受约束的两条边之间的中心。

约束偏差范围从0.0(0%)到1.0(100%),水平约束偏差从左到右增长,而垂直约束偏差从上到下增长。约束偏差对于为不同的屏幕尺寸动态定位视图很有用。

要更轻松地了解约束偏差的工作原理,请切换回设计视图。选中 Raze Galactic TextView,在右侧查看 view inspector (视图查看器) 以及各种 Attributes(属性)。

视图查看器左侧的垂直滑块控制垂直约束偏差,底部的垂直滑块控制水平约束偏差。拖动每个滑块以查看约束偏差的工作原理:

在继续下一步之前,将水平和垂直约束的偏差重置为50%。

<br><br>

对齐左边缘并垂直分布

接下来,同时选择所有TextViewGoogle Sign-In(从谷歌登录) 和 Sign-Up (注册) 按钮。你可以按住Shift键单击每个UI元素依次全部选中它们。然后,从工具栏中单击 Align (对齐) 并选择 Left Edges (左边缘) 。

这是应用左对齐后的布局:

你注意到这里有什么问题了吗? Raze Galactic TextView的水平约束变不见了。

左边缘将视图与所选择的最左侧视图对齐,它实际上做的是按降序从一个视图创建左约束依赖关系,以最底部的视图充当锚点。

因此,要使 Left Edges (左边缘)命令起作用,它必须删除其余所选视图的现有水平约束。

要反转约束依赖关系顺序,请再次应用 Left Edges (左边缘)命令。您可以看到约束箭头现在指向上方。

现在,使用与上一步骤中选择的相同UI元素,单击 Pack (包) 菜单并选择 Distribute Vertically (垂直分布) 。

垂直分布后,你的屏幕将如下所示:

同样,你可能已经注意到与上述类似的问题:连接火箭图标底部和 Raze Galactic TextView的约束属性已经消失。

约束依赖性按降序排列,就像在第一个 Left Edges (左边缘) 命令之后一样。不幸的是,没有简单的方法来解决这个问题,因此您必须手动创建垂直分布式约束。

本教程将介绍如何在下一部分中执行此操作,因此请继续并撤消 Distribute Vertically (垂直分布) 命令。

注意:AlignPack 命令可能无法正常工作。它们可能会删除现有的约束,并且可能不会移动某些受约束的视图。在应用这些命令之前和之后,请务必检查受影响视图的约束。

<br><br>

使用默认边距

要创建垂直分布式约束,只需连接具有相同边距的约束即可。快速完成此操作的一个技巧是使用 Default Margin (默认边距) 工具。

现在,单击 Default Margin (默认边距) 按钮并设置值为60dp,然后将Google登录按钮的顶部约束连接到 Raze Galactic TextView的底部。你会发现Google登录按钮会自动变换位置,在它与 Raze Galactic TextView之间留出60dp的间距。 魔法 :]

创建其余的垂直约束,如上面的GIF所示。最后,将到 Raze Galactic TextView的左侧约束到火箭图标的右侧,边距为30dp。

检查组件树以查看是否存在其他错误。如果没有错误,恭喜!

构建并运行你的应用程序,现在所有内容应该在模拟器中以适当的布局显示了。

<br><br>

接下来做什么?

你可以使用本教程顶部的下载材质按钮下载此项目的最终版本。

ConstraintLayout 在布局编辑器中构建UI 可能会令人沮丧,因为某些工具不够智能。但是,如果如果你知道如何正确的去使用,则可以节省大量时间。

本教程中未提及其他布局编辑器工具,你可以去使用它们来了解它们的工作原理。看看谷歌ConstraintLayout的文档上,以了解更多信息。

有关更复杂的ConstraintLayout示例,请参阅我们的后续文章ConstraintLayout Android教程:复杂布局

要查看更多ConstraintLayout的示例,请查看我们的 Android Apprentice 一书,该书使用ConstraintLayout作为所有页面的布局。

你现在已经掌握了ConstraintLayout基本的概念,要了解更多高级功能并获得处理复杂布局的提示,请继续关注我们即将推出的构建复杂布局的教程ConstraintLayout Android教程:复杂布局,你将在其中为 Raze Galactic 旅行应用程序构建更复杂的约束视图,然后为其制作动画!

如果你有任何问题或意见,欢迎在下方留言讨论~