本文是 ConstraintLayout 小课堂系列第 1 讲,课程目录:
布局要求
先上最终效果图:
- 这是一个 Item 布局,分为两行,第一行显示可变长度的文字,第二行显示固定长度的文字,底部还有一个粗线条条。
- 绿色背景的文字语义上是一个标签,可以不显示,也就是设置 visibility=gone,这时右侧的长文字要占据整个宽度。
- 长文字与标签的间距是 10dp,而标签距离 parent 的间距是 16dp,隐藏掉标签后长文字要左移占据标签的位置,距离 parent 要保持 16dp 的间距。
- 上下两行的高度是固定的 40dp,行内文字垂直居中对齐。
实现思路
首先要找到布局的起点,然后创建其他 View 与起点的关系,一点一点将所有 View 放在正确的位置。
整个布局被分成上下两行,并且两行都是固定高度,内部的 View 在行内居中。可以用两个 ConstraintLayout 的 Guideline 来固定在行的中间位置,通过居中对齐的方式将 View 与 Guideline 对齐。
详解
如何居中布局
ConstraintLayout 并不是靠一个单独的属性设置居中,而是用两侧的约束像弹簧一样将 View 拉起来浮在中间。
垂直居中需要将 layout_height
设置为 wrap_content
,然后上下两个边界设置为同一个对象:Bottom_toBottomOf
、Top_toTopOf
。同理水平居中需要设置 layout_width
、Start_toStartOf
、End_toEndOf
。
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" 👉 水平线还是垂直线
app:layout_constraintGuide_begin="20dp" /> 👉 begin 表示距 parent top 多长
<TextView
android:id="@+id/title"
android:layout_height="wrap_content" 👉 重点,居中必须设置为 wrap_content
app:layout_constraintBottom_toBottomOf="@+id/guideline" 👉 两边都要设置才能居中
app:layout_constraintTop_toTopOf="@+id/guideline" 👉 两边都要设置才能居中
... />
如何用约束设置尺寸
layout_wdith
或 layout_height
设置为 0dp
表示匹配约束的含义。下面是长文字设置为匹配剩余宽度的代码:
<TextView
android:id="@+id/title"
android:layout_width="0dp" 👉 重点,匹配约束必须“放弃宽度”,设置为 0dp
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:ellipsize="end" 👉 末尾显示 ...
android:lines="1" 👉 只显示一行,防止撑大高度
android:text="Update 3rd libs, and submit app for review."
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/tag" />
居中与匹配约束两种布局,一个是保持 View 原有尺寸放置在中间,一个是拉伸 View 直到符合约束。其实约束相关的属性设置是一样的,不一样的是尺寸属性,layout_width
或 layout_height
,设置的值是 wrap_content
还是 0dp
。
Guideline 是一条水平线或者垂直线,可设置距离 ConstraintLayout 边缘的大小,在约束布局中可以像普通 View 一样用来定义关系。本例中用来当做垂直居中对齐的锚点。
如何对齐文字的 Baseline
垂直对齐除了 top 和 bottom 以外,还有 baseline,只对含有文字的 View
生效,比如 TextView
,表示两个 TextView
的文字基准对齐,获得更美观的排版效果。
<TextView
android:id="@+id/tag"
android:text="iOS app"
app:layout_constraintBaseline_toBaselineOf="@+id/title" 👉 baseline 对齐
... />
如何处理 GONE 了的 View
神奇的 ConstraintLayout 完全可以自动处理 visibility=gone 的 View。处理的规则是:
- 这个 View 被缩小为一个尺寸为 0×0 的点。
- 这个 View 的 margins 值都会变成 0。
- 与其他 View 之间的约束仍然生效。
<TextView
android:id="@+id/tag"
android:visibility="gone" 👉 消失了
android:layout_marginStart="16dp"
... />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:layout_constraintStart_toEndOf="@+id/tag" 👉 tag消失了,我贴上来
/>
按照规则,TextView(tag) 的宽度变为 0,并且 margin 变为 0,于是 TextView(title) 被拉伸到了左边与 parent 左对齐,这时候与 parent 的左边距是它自己的 layout_marginStart="10dp"
。
结果与设计不符了,这时候就要使用一组神奇的属性:goneMargin:layout_goneMarginStart
、layout_goneMarginBottom
等等。表示如果我通过约束连接了一个 gone View,我这个方向上的 margin 由 goneMargin 属性替代。
因此这里添加一下 goneMargin 属性就达到了想要的效果:
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
app:layout_constraintStart_toEndOf="@+id/tag"
👇 constraintStart 指向的 View gone 了,于是 goneMarginStart 说了算了
app:layout_goneMarginStart="16dp"
/>
本节课程内容小节
- 如何居中布局
- 如何用约束设置尺寸
- 如何对齐文字的 Baseline
- 如何处理 GONE 了的 View
全部代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">
<View
android:id="@+id/first_line"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="#0099aa"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp" />
<View
android:id="@+id/second_line"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="#992277"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/first_line"
tools:layout_editor_absoluteX="0dp"
tools:layout_editor_absoluteY="0dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="20dp" />
<TextView
android:id="@+id/tag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:background="#009000"
android:padding="6dp"
android:text="iOS app"
android:textColor="@android:color/white"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintBaseline_toBaselineOf="@+id/title"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:ellipsize="end"
android:lines="1"
android:text="Update 3rd libs, and submit app for review."
android:textSize="17sp"
app:layout_constraintBottom_toBottomOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/tag"
app:layout_constraintTop_toTopOf="@+id/guideline"
app:layout_goneMarginStart="16dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="60dp" />
<TextView
android:id="@+id/startTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="27dp"
android:text="13:10"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2"
app:layout_constraintBottom_toBottomOf="@+id/guideline2" />
<TextView
android:id="@+id/connector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="-"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/startTime"
app:layout_constraintTop_toTopOf="@+id/startTime" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="145dp"
android:text="|"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/startTime" />
<TextView
android:id="@+id/endTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="16:15"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/connector"
app:layout_constraintTop_toTopOf="@+id/startTime" />
<TextView
android:id="@+id/interval"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="03:05"
android:textSize="18sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/separator"
app:layout_constraintTop_toTopOf="@+id/startTime" />
<View
android:id="@+id/colorBar"
android:layout_width="100dp"
android:layout_height="6dp"
android:layout_marginTop="14dp"
android:background="#009900"
app:layout_constraintLeft_toLeftOf="@+id/interval"
app:layout_constraintTop_toBottomOf="@+id/guideline2" />
</androidx.constraintlayout.widget.ConstraintLayout>
(ole)
网友评论