哇哦,我们又有一整天时间,所以就来学点酷炫的新知识吧。
你们好,希望各位都有所进步。在上周中,我们学习了 ConstraintLayout 的第一部分。现在是时候来学习这个神奇布局的剩下内容了。
动机: 学习动机与先前在第一部分中讨论的是一样的。 不过这次我不准备解释 ConstraintLayout 的特性,相反,我会分享一些当你们独立实现时可能遇到的问题。最后,我向大家保证,你们将会潜移默化地了解所有(我知道的)概念。
问题:
是时候开始了!:)
我们需要下载 2.3 版本的 Android studio。先前版本的可视化编辑器不太完善,有时会在面板上显示错误的信息。所以下载 2.3 测试版本是非常重要的,该版本在我写这篇文章时已经可以获取到了。
1. MATCH_PARENT 不起作用:
当你在 ConstraintLayout 中试图设置长宽为 match_parent 时,如下图所示,将不会起作用(编辑器会自动修正)。
不要再用 match_parent。记住 match_parent 不是被废弃了,而是从 ConstraintLayout 嵌套的视图中移除掉了。
解决方案:
恰当地在 Constrain Layout 嵌套的视图中使用 parent 属性。就像我们在 RelativeLayout 中设置 width=0dp,然后对齐到父布局的左右两边一样,我们需要做同样的操作,如下图所示:
2. 居中对齐视图 (水平, 垂直, 在父视图中心):
我们需要在父布局的中心放置一个按钮,能通过下图的操作实现:
现在我坚信,你能轻易地自己实现水平和垂直居中了。:)
3. 怎样将视图从中心向左或右移动一些 DP 值:
大部分设计师都给我们提过奇怪的需求,比如有人想要一段文字不是 100% 居中的,而是几乎从中心开始的。
解决方案:
首先, 抱歉了设计师
同样地,你可以使用 app:layout_constraintVertical_bias=”.1″. 记住取值区间是 0,0.1 .. 1。
4. 管理图片视图的比例:
很多时候我们的 ImageView 都有特定的比例,比如 4:3,因此我们能用下图的方式实现:
哈哈!我知道这很简单,但还有另外一个问题。比如我有一个宽高尺寸都是 match_constrained 类型的 TextView,但是我希望整个 textView 的形状适应设备大小为方型。一个关键点是,我们需要按如下方式设置宽高属性来约束为方型:
现在你可以随机地尝试更多设置值了。
5. 需要两列或多列:
现在我们的设计师又要求像是表格布局的样式,像是下面这样的两列:
解决方案:
实现起来非常简单。我们只需在 ConstraintLayout 中添加一个叫做 Guidelines 即可。这非常酷!你能马上搞定。 你可以将这些线条主要用作分隔 UI 的辅助工具。如果你说你掌握了 Guidelines 的话,你必须知道下面三个重要的属性:
orientation: 水平, 垂直 // 分隔屏幕的方式
layout_constraintGuide_percent: 0, 0.1 .. 1 // 屏幕的全部大小表示为 1.0
layout_constraintGuide_begin: 200dp // 通过 dp 值来表示放置 Guidelines 的位置
最终,Guidelines 永远不会绘制到 UI 界面中。 首先,我先来实现一个将屏幕分隔为两部分的 Guidelines ,以便我能看到两列内容。
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent=".5" />
首先添加第一个按钮:
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
添加第二个按钮:
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@+id/button" />
接下来在第二列中添加 Textview:
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/colorAccent"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
使用 ConstraintLayout 实现这样的 UI 效果非常简单。使用这个方法,你可以随意添加更多的行和列。
完整的代码如下:
<?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">
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent=".5"/>
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/guideline"
app:layout_constraintTop_toBottomOf="@+id/button" />
<TextView
android:id="@+id/textView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/colorAccent"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
6. 父视图的左边距, 一些是 16dp ,一些是 8dp:
我有一些视图,其中一些左边距是 16dp,一些是 8dp。如下所示:
也许你会问这样的问题:为什么这篇文章中会提到这么简单的效果?主要是因为我在分享一些管理 UI 布局的技巧,我觉得你应该知道怎样用不同的方式来实现效果。
所以是时候开始了。
如果你由上至下地看下来,首先,第二个和最后一个视图外边距为 16dp,其余的外边距为 8dp。
我能够直接设置所有视图的外边距,但是许多时候设计师说这样在某些小屏设备上看起来很丑,能否将这些视图都设置成 8dp 到 12dp 的外边距,并且将所有 16dp 的外边距改为 12dp。
如果你直接设置外边距,那简直就是噩梦 。所以我将要设置两条辅助线,一个边距是 8dp,另一个边距是 16dp。两个都是垂直方向的。
<android.support.constraint.Guideline
android:id="@+id/eightDpGuideLine"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="8dp" />
<android.support.constraint.Guideline
android:id="@+id/sixteenDpGuideLine"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="16dp" />
现在只需要将所有视图的外边距都设置成 0dp 就可以很轻松地实现需求了。下面会给出完整的代码:
<?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">
<android.support.constraint.Guideline
android:id="@+id/eightDpGuideLine"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="8dp"/>
<android.support.constraint.Guideline
android:id="@+id/sixteenDpGuideLine"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="16dp"/>
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="38dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="@+id/sixteenDpGuideLine"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="99dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="@+id/eightDpGuideLine"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="75dp"
android:text="Button"
app:layout_constraintLeft_toLeftOf="@+id/sixteenDpGuideLine"
app:layout_constraintTop_toBottomOf="@+id/button3" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="115dp"
android:text="TextView"
app:layout_constraintLeft_toLeftOf="@+id/eightDpGuideLine"
app:layout_constraintTop_toBottomOf="@+id/button4" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="86dp"
app:layout_constraintLeft_toLeftOf="@+id/sixteenDpGuideLine"
app:layout_constraintTop_toBottomOf="@+id/button5" />
</android.support.constraint.ConstraintLayout>
现在设计师想要把 16dp 改成 20dp。我只需要改变 Guideline 值即可: app:layout_constraintGuide_begin=”16dp” 变为 app:layout_constraintGuide_begin=”20dp”。另外值得注意的是:要及时修改命名以免给你的同事造成困惑。例如这里我会及时将命名由 sixteenDpGuideLine 修改成 twentyDpGuideLine。现在你可以看到下图中神奇的变化:
7. 怎样在 ConstraintLayout 中实现 LinearLayout:
我们现在有三个按钮,水平均分并排着。在 LinearLayout 中我们可以通过 weight 来实现,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Button1" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Button2" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Button3" />
</LinearLayout>
怎样在 ConstraintLayout 中实现这种效果呢? 非常简单,直接看代码:
<?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">
<Button
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/button2" />
<Button
android:id="@+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toRightOf="@+id/button1"
app:layout_constraintRight_toLeftOf="@+id/button3" />
<Button
android:id="@+id/button3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintLeft_toRightOf="@+id/button2"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
这样就得到了同样的效果。只需关注一点,在这些按钮中我建立了两两之间的关系,并且设置 width=”0dp”。
android:id="@+id/button1"
........
app:layout_constraintRight_toLeftOf="@+id/button2"
android:id="@+id/button2"
........
app:layout_constraintLeft_toRightOf="@+id/button1"
app:layout_constraintRight_toLeftOf="@+id/button3"
........
噢不,你们已经学到了一个新的概念叫做 chaining。当我们建立视图之间的两者关系时,编辑器会自动链接起来。现在是时候来讨论下使用 chaining 带来的好处了。在这之前,我想要你先了解 chaining 在编辑器中的样子:
在下文我将复制一些来自 Android 官方的文档。因为我觉得解释得很好。
Chains:
Chains 在一个方向(水平或垂直)提供类似组合的行为。另一方向可以独立约束。
创建 chain:
一系列控件通过建立双向联系从而链接成链 (看下面,展示了一个含有两个控件的最小链)。
Chain 的头部
Chain 被位于它链中第一个元素的属性集控制 (链的“头”部):
对于水平链来说最左边的控件是头部,对垂直链来说最上面的控件是头部。
现在我觉得你们应该熟悉了 Chaining 的概念了。接下来我会介绍关于 chaining 的另一个知识点:chaining style。本来有一个非常好的文档来介绍它,但我决定稍后再推荐,因为它会把你搞混淆。首先,我先来让你们掌握些实际经验。
对于 chaining style 来说,有一个新的属性 layout_constraintHorizontal_chainStyle (layout_constraintVertical_chainStyle) 我们能给这个属性设置五种值。
Spread Chain, Spread Inside Chain, Packed Chain, Packed Chain with Bias 以及 Weighted Chain。下面将一一介绍每一种值。
Spread Chain:
通过在头部视图的属性中添加 “spread”,得到如下的结果。
app:layout_constraintHorizontal_chainStyle="spread"
并没有发生变化,因为 **spread ** 就是默认值。
Spread Inside Chain:
在头部视图中添加 “spread inside”,得到如下结果:
简而言之当我的头部视图中设置这个值时,链头和尾部的视图都自动地依附到了父容器的左右两边。如果你想要这种效果,那就应该使用 “spread_insdie” 值。
Packed Chain:
在头部视图中添加 “packed”,得到如下结果:
如果我们想要所有的视图连在一起,我们就应使用 “packed” 属性。需要注意一点,所有的视图会默认变为水平居中。现在我的问题是我不想要水平居中的效果,那么就轮到下个属性了。
Packed Chain with Bias:
在头部视图中添加 “packed and horizontal bias”,得到如下结果:
通过使用偏移量属性,我能随意地修改位置。
Weighted Chain:
比如我有三个按钮,前两个要占半个屏幕,第三个占据剩下的一半屏幕。对于这种需求,我将要使用到 weighted chain 概念,如下所示。一个关键点是,通常来说,我们使用默认的 “spread” 属性,然后添加一个“layout_constraintHorizontal_weight” 属性来管理视图空白的分布。
现在我们了解了 Chaining 的概念以及 chaining styles 的不同。接下来我将要复制一些关于样式的定义:
CHAIN_SPREAD
— 元素将被展开 (默认样式)- Weighted chain — 在
CHAIN_SPREAD
模式下, 如果控件被设置成MATCH_CONSTRAINT
, 它们将会分割剩余空间 CHAIN_SPREAD_INSIDE
— 同样地, 但是链的端点不会被展开CHAIN_PACKED
—链的元素将会被拼接,子元素的水平或垂直偏移量会影响拼接后整体的位置
Weighted chains:
链的默认样式是展开并均分剩余空间。如果一个或多个元素使用 MATCH_CONSTRAINT
,它们将会使用剩余空间(平均分配剩余空间)。 layout_constraintHorizontal_weight
属性和 layout_constraintVertical_weight
属性将会控制类型为MATCH_CONSTRAINT
的元素如何分配剩余空间。例如, 一条链上有两个元素使用 MATCH_CONSTRAINT
, 第一个元素的权重值是 2 第二个元素的权重值是 1, 那么第一个元素占据的空间将是第二个元素的两倍。
8. 隐藏视图后,布局遭到破坏:
在运行时,某些视图隐藏之后会发生什么呢?我做了一些实验并得到了奇怪的结果。 为了解释并解决这些问题,我用了一个非常简单但有效的例子,例如我有如下两个按钮:
根据编写的代码,当第二个按钮点击时,第一个按钮会隐藏。当我实现这个代码时,我猜想第二个按钮会移动到父容器的左边缘,让我们来看看发生了什么。
哈哈我的设想被推翻了!
解决方案:基本上来说,ConstraintLayout 中有一个新的属性叫做 “app:layout_goneMargin”。通过使用这个属性,我能解决这种问题。因此我将添加一两行代码然后看看我的问题解决没。
砰!如期所至!好耶。
好啦各位,该说再见啦。下期再见!