ConstraintLayout

参考

https://blog.csdn.net/guolin_blog/article/details/53122387

https://developer.android.com/training/constraint-layout

https://juejin.im/post/5d12c4146fb9a07ea33c24b7

简介

constraint 美 /kənˈstreɪnt/

约束布局(ConstraintLayout)通过使用 Android Studio 中的可视化编辑器来为您生成绝大多数的 UI,进而达到简化 Android 中创建复杂布局的目的。它通常被我们描述为更加强大的 RelativeLayout。通过使用约束布局,您可以定义一些复杂的布局而不需要创建复杂的视图层级。

下图是ConstraintLayout的layout的design界面

wps39

下图是ConstraintLayout的子view的参数界面

wps40

使用

导入

implementation 'com.android.support.constraint:constraint-layout:1.1.3'

子view没有设置约束时

你在ConstraintLayout中加入的子view,如果

l 水平方向没有约束,在运行时子view的位置会紧靠在ConstraintLayout的左边。

l 垂直方向没有约束,在运行时子view的位置会紧靠在ConstraintLayout的上边。

而不会是layout design界面显示的位置。

layout_constraintXXX_toXXX

这些属性用来控制ConstraintLayout中加入的子view的上下左右的约束/对齐。

值都是 当前ConstraintLayout内的某个view的id,或parent。

另外,layout_constraintXXX_toXXX是尽量让view和其他view某边对齐,并不会调整view的大小强制让边界对齐。

例如:

layout_constraintLeft_toLeftOf="parent"

layout_constraintRight_toRightOf="parent"

显示的是水平方向居中,当然会受到layout_constraintHorizontal_bias的控制。

l layout_constraintLeft_toLeftOf

l layout_constraintStart_toStartOf

A的左边和B的左边对齐,

l layout_constraintLeft_toRightOf

l layout_constraintStart_toEndOf

l layout_constraintRight_toLeftOf

l layout_constraintEnd_toStartOf

l layout_constraintRight_toRightOf

l layout_constraintEnd_toEndOf

l layout_constraintTop_toTopOf

l layout_constraintTop_toBottomOf

l layout_constraintBottom_toTopOf

l layout_constraintBottom_toBottomOf

l layout_constraintBaseline_toBaselineOf

用一张图来说明

wps41

在layout design中

wps42

通过拖动view的小圆点到其他view的某个原点来设置约束。

layout_constraintXXX_bias

当同时设置水平/垂直的两边时,这个参数才有效。

如果没有设置这两个参数时,表示的是0.5,

l layout_constraintHorizontal_bias

表示view在左右限制的范围内的位置,0表示view紧挨左边,1表示紧挨右边。

l layout_constraintVertical_bias

和上边的类似。0表示view紧挨上边,1表示紧挨下边。

在layout design中的设置:

wps43

子view的几种大小

wps44

中间的那个正方形区域,它是用来控制控件大小的。一共有三种模式可选,每种模式都使用了一种不同的符号表示,点击符号即可进行切换。

wps45layout_width/height设置为wrap content

当子view的 layout_width 或者 layout_height 为 wrap_content 时,可以使用对应的 android:minXXX 与 android:maxXXX 来限制控件的尺寸。

如果说想使用ConstraintLayout的相关属性来限制大小(如下)的话,需要app:layout_constrainedWidth\Height设置为true。

另外,而如果有同时设置 android:maxHeight 与 app:layout_constrainedWidth_max,则不管有没有设置 app:layout_constrainedWidth,都会强制生效 android:maxHeight ,而忽略app:layout_constrainedWidth_max的影响。

wps46layout_width/height设置为match_constraint时,

即设置宽或高为0dp,同时使用对应水平或垂直方向约束。

它有点类似于match parent,但和match parent并不一样,是属于ConstraintLayout中特有的一种大小控制方式,

区别在于,match parent是用于填充满当前控件的父布局,而match_constraint是用于填充满当前控件的约束规则。

在1.1版本后,可以通过以下属性改变控件的行为:

l app:layout_constraintWidth_max/min

l app:layout_constraintHeight_max/min

l app:layout_constraintWidth_percent 宽度占水平约束范围的百分比

l app:layout_constraintHeight_percent

l app:layout_constraintWidth_default:有三个值:

wrap:等价于 android:layout_width=”wrap_content”

spread:和不设置一样,占满控件的约束范围。

percent:设置 View 的宽度为 parent 的比例值,比例值默认是100%,即宽度是match_parent。

l app:layout_constraintHeight_default

这三种值尽量在一个控件中只是用一种,

wps47layout_width/height设置为固定值

比例约束

控件的宽高比,要求是宽或高至少一个设为0dp,然后设置属性layout_constraintDimensionRatio即可。

<TextView

    android:text="Hello"

    app:layout_constraintDimensionRatio="3:1"

    android:layout_width="0dp"

    android:layout_height="100dp"

    />

 

这里设置宽高比为3:1,高度为100dp,那么宽度将为300dp。

也可以在比例前加H表示是宽高比,W表示是高宽比。

如果宽高都为0,则使用view的约束范围。

l 宽高比,则先确定宽度,在通过比值算出高度。

l 高宽比,相反。

如下面表示宽高比。

<Button

android:layout_width="0dp"

android:layout_height="0dp"

    app:layout_constraintDimensionRatio="H,16:9"

    app:layout_constraintBottom_toBottomOf="parent"

    app:layout_constraintTop_toTopOf="parent"/>

 

 

在layout design中自动添加约束

内容变得复杂起来,给每个控件一个个地添加约束也是一件很繁琐的事情。为此,ConstraintLayout中支持自动添加约束的功能,可以极大程度上简化那些繁琐的操作。

wps48

l 磁铁小按钮表示自动连接约束,

打开后,在拖动一个view,之后再合适位置松手后会自动添加约束,不过这种自动添加的约束规律不明显,所以默认是关闭的。

l 另一个按钮表示Inference(推理) Constraint

AutoConnect只能给当前操作的控件自动添加约束,而Inference会给当前界面中的所有元素自动添加约束。因而Inference比较适合用来实现复杂度比较高的界面,它可以一键自动生成所有的约束。

Margins

android:layout_marginXXX

原本的 android:layout_marginXXX 系列。不多说。

需要注意的是如果设置了某个方向的 margin 值,那么只有设置相对应方向的约束条件,这个方向的 margin 值才生效。

比如设置了android:layout_marginStart="20dp",那么当且仅当设置了 start 的约束条件app:layout_constraintStart_toStartOf或者app:layout_constraintStart_toEndOf这个 margin 值才生效。

还要注意的是ConstraintLayout不支持负数的 margin,如果是负数的话效果和 0 一样的,当然负数的 padding 是没问题的。

app:layout_goneMarginXXX

作用是控制当前 View 所参考的 View 为 GONE 的时候的 margin 值,如果参考的 View不为 GONE 则不起作用。

l layout_goneMarginBottom

l layout_goneMarginEnd

l layout_goneMarginLeft

l layout_goneMarginRight

l layout_goneMarginStart

layout_goneMarginTop

注意:所参考的 View 为 GONE 的时,此view的宽高、margin、padding都失效,但它的layout_constraint属性依然有效。

圆形定位(Added in 1.1)

圆形定位指的是View的中心点相对于另外View中心点的位置。贴张官网图。

wps49

即表示B设置的,以A的中心为原点,半径是layout_constraintCircleRadiu,从0度(时钟0刻度的位置为0角度)顺时针旋转layout_constraintCircleAngle,B的中心位置就在这。

涉及三个属性:

l layout_constraintCircle : 引用另外一个view的id,上图的A view

l layout_constraintCircleRadius : 半径,上图的radius

l layout_constraintCircleAngle : 角度,角度以时钟0刻度的位置为0角度,顺时针为正。

吃个栗子:

<TextView

    ...

    android:text="Hello"

    android:id="@+id/tvHello"

    app:layout_constraintTop_toTopOf="parent"

    app:layout_constraintBottom_toBottomOf="parent"

    app:layout_constraintLeft_toLeftOf="parent"

    app:layout_constraintRight_toRightOf="parent"/>

<TextView

    android:text="World"

    app:layout_constraintCircle="@id/tvHello"

    app:layout_constraintCircleRadius="180dp"

    app:layout_constraintCircleAngle="135"/>

 

效果图:Hello中间居中,World 135角度

wps50

Chain

链在水平或者垂直方向提供一组类似行为。如图所示可以理解为横向链。这里需要了解一点,A与parent的左边缘约束,B与parent的右边边缘约束,A右边和B左边之间相互约束,才能使用一条链。多个元素之间也是如此,最左最右与parent约束,元素之间边相互约束。不然下面的链式永远无法生效。

wps51

横向链最左边第一个控件,垂直链最顶边第一个控件称为链头,可以通过下面两个属性链头统一定制链的样式。

l layout_constraintHorizontal_chainStyle 水平方向链式

l layout_constraintVertical_chainStyle 垂直方向链式

它两的值可以是

l spread:视图是均匀分布的(考虑到边距margins 后)。 这是默认值。

l spread_inside:第一个和最后一个视图附加到链的每一端的约束上,其余视图均匀分布。

l packed:视图打包在一起(在考虑了边距之后)。可以通过修改chain head view的偏移bias,来改变packed views的位置。

wps52

l Weighted:并没有这个选项,是当使用spread或spread_inside时的额外说明,如果有view设置了对应方向的MATCH_CONSTRAINT(0dp),那他们将填充剩余的空间。默认情况下会将剩余空间平分给那些设置了MATCH_CONSTRAINT(0dp)的view,但可以通过在view上加入layout_constraintHorizontal_weight 或layout_constraintVertical_weight属性,来控制权重,这个特性和LinearLayout的weight类似。

1.1之后,在链中使用边距时,边距是相加的,也就说,假设Hello的右边距为5,World的左边距为20,那么它们之间的边距就是25。在链式,边距先从剩余空间减去的,然后再用剩余的空间在元素之间进行定位。

其他的工具

Guideline

Guideline继承自view,实际运行时大小是0,并且是gone的,它的作用是作为参考线,辅助其他view的位置摆放,即可以成为其他view的layout_constraintxxx的引用。

在xml中

<android.support.constraint.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />

 

Guideline的几个属性:

l app:layout_constraintGuide_begin:距离ConstraintLayout左边的大小,单位时dp,

l app:layout_constraintGuide_end:距离ConstraintLayout右边的大小,单位时dp,

l app:layout_constraintGuide_percent:距离ConstraintLayout左边的百分比,用小数表示,紧挨ConstraintLayout左边为0,紧挨ConstraintLayout右边为1,

同时只能用一个来表示Guideline的位置。

l android:orientation:表示是水平参考线,还是垂直。

在layout design界面添加Guideline的方式:

wps53

wps54

Barrier

Barrier有点类似Guideline,但Barrier会根据所有引用的控件尺寸的变化重新定位。例如经典的登录界面,右边的EditText总是希望与左右所有TextView的最长边缘靠齐。 如果两个TextView其中一个变得更长,EditText的位置都会跟这变化,这比使用RelativeLayout灵活很多。

l app:barrierDirection所引用控件对齐的位置,可设置的值有:bottom、end、left、right、start、top.

l constraint_referenced_ids为所引用的控件,例如下边例子的tvPhone,tvPasswrod。

wps55

代码:

<android.support.constraint.ConstraintLayout

        xmlns:android="http://schemas.android.com/apk/res/android"

        xmlns:app="http://schemas.android.com/apk/res-auto"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

    <android.support.constraint.Barrier

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

app:barrierDirection="right"

            android:id="@+id/barrier"

app:constraint_referenced_ids="tvPhone,tvPassword" />

    <TextView android:layout_width="wrap_content"

              android:text="手机号码"

android:id="@+id/tvPhone"

              android:gravity="center_vertical|left"

              android:padding="10dp"

              android:layout_height="50dp"/>

    <TextView android:layout_width="wrap_content"

              android:text="密码"

              android:padding="10dp"

              android:gravity="center_vertical|left"

android:id="@+id/tvPassword"

              app:layout_constraintTop_toBottomOf="@id/tvPhone"

              android:layout_height="wrap_content"/>

    <EditText android:layout_width="wrap_content"

              android:hint="输入手机号码"

              android:id="@+id/etPassword"

              app:layout_constraintLeft_toLeftOf="@id/barrier"

              android:layout_height="wrap_content"/>

    <EditText android:layout_width="wrap_content"

              android:hint="输入密码"

              app:layout_constraintTop_toBottomOf="@id/etPassword"

              app:layout_constraintLeft_toLeftOf="@id/barrier"

              android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>

 

Group

用来控制一组view的可见性,如果view被多个Group控制,则以最后的Group定义的可见性为主。

吃个香喷喷栗子吧:

Group默认可见时,是这样的。

wps56

设置Group的visible属性为gone.

<android.support.constraint.ConstraintLayout

        xmlns:android="http://schemas.android.com/apk/res/android"

        xmlns:app="http://schemas.android.com/apk/res-auto"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

    <android.support.constraint.Group

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:id="@+id/group"

            android:visibility="gone"

            app:constraint_referenced_ids="tvPhone,tvPassword"

            />

    <TextView android:layout_width="wrap_content"

              android:text="手机号码"

              android:id="@+id/tvPhone"

              android:gravity="center_vertical|left"

              android:padding="10dp"

              android:layout_height="50dp"/>

    <TextView android:layout_width="wrap_content"

              android:text="密码"

              android:padding="10dp"

              android:gravity="center_vertical|left"

              android:id="@+id/tvPassword"

              app:layout_constraintLeft_toRightOf="@id/tvPhone"

              app:layout_constraintTop_toBottomOf="@id/tvPhone"

              android:layout_height="wrap_content"/>

    <TextView android:layout_width="wrap_content"

              android:text="GitCode"

              android:padding="10dp"

              android:gravity="center_vertical|left"

              app:layout_constraintLeft_toRightOf="@id/tvPassword"

              android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>

 

效果就变成了这样了,tvPhone,tvPassword都被隐藏了。

wps57

Placeholder(占位符)

一个view占位的占位符,当指定Placeholder的content属性为另一个view的id时,该view会移动到Placeholder的位置。

xml中,将TextView的定位在屏幕中间,接着将id设置给Placeholder的属性后,TextView的位置就跑到Placeholder所在的地方。

<android.support.constraint.ConstraintLayout

        xmlns:android="http://schemas.android.com/apk/res/android"

        xmlns:app="http://schemas.android.com/apk/res-auto"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

    <android.support.constraint.Placeholder

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            app:content="@id/tvGitCode"

              />

    <TextView android:layout_width="wrap_content"

              android:text="GitCode"

              android:id="@+id/tvGitCode"

              android:padding="10dp"

              app:layout_constraintLeft_toLeftOf="parent"

              app:layout_constraintRight_toRightOf="parent"

              android:gravity="center_vertical|left"

              android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>

 

布局参数

<declare-styleable name="ConstraintLayout_Layout">

    <attr name="android:orientation" />

    <attr name="android:minWidth" />

    <attr name="android:minHeight" />

    <attr name="android:maxWidth" />

    <attr name="android:maxHeight" />

<attr name="layout_optimizationLevel">

    <flag name="none" value="1"/>

    <flag name="all" value="2"/>

    <flag name="basic" value="4"/>

    <flag name="chains" value="8"/>

</attr>

    <attr name="constraintSet" format="reference"/>

    <attr format="dimension" name="layout_constraintGuide_begin"/>

<attr format="dimension" name="layout_constraintGuide_end"/>

<attr format="float" name="layout_constraintGuide_percent"/>

    <attr format="reference|enum" name="layout_constraintLeft_toLeftOf">

<enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintLeft_toRightOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintRight_toLeftOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintRight_toRightOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintTop_toBottomOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintTop_toTopOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintBottom_toTopOf">

    <enum name="parent" value="0"/>

</attr>

<attr name="layout_constraintBottom_toBottomOf" format="reference|enum">

    <enum name="parent" value="0"/>

</attr>

<attr name="layout_constraintBaseline_toBaselineOf" format="reference|enum">

    <enum name="parent" value="0"/>

</attr>

    <attr format="reference|enum" name="layout_constraintStart_toEndOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintStart_toStartOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintEnd_toStartOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="reference|enum" name="layout_constraintEnd_toEndOf">

    <enum name="parent" value="0"/>

</attr>

<attr format="dimension" name="layout_goneMarginBottom"/>

<attr format="dimension" name="layout_goneMarginEnd"/>

<attr format="dimension" name="layout_goneMarginLeft"/>

<attr format="dimension" name="layout_goneMarginRight"/>

<attr format="dimension" name="layout_goneMarginStart"/>

<attr format="dimension" name="layout_goneMarginTop"/>

<attr format="float" name="layout_constraintHorizontal_bias"/>

<attr format="float" name="layout_constraintVertical_bias"/>

<attr name="layout_constraintWidth_default">

    <enum name="spread" value="0"/>

    <enum name="wrap" value="1"/>

</attr>

<attr name="layout_constraintHeight_default">

    <enum name="spread" value="0"/>

    <enum name="wrap" value="1"/>

</attr>

<attr format="dimension" name="layout_constraintWidth_max"/>

<attr format="dimension" name="layout_constraintWidth_min"/>

<attr format="dimension" name="layout_constraintHeight_max"/>

<attr format="dimension" name="layout_constraintHeight_min"/>

<attr format="integer" name="layout_constraintLeft_creator"/>

<attr format="integer" name="layout_constraintTop_creator"/>

<attr format="integer" name="layout_constraintRight_creator"/>

    <attr name="layout_constraintBottom_creator" format="integer"/>

    <attr name="layout_constraintBaseline_creator" format="integer"/>

<attr format="string" name="layout_constraintDimensionRatio"/>

<attr format="float" name="layout_constraintHorizontal_weight"/>

<attr format="float" name="layout_constraintVertical_weight"/>

<attr format="enum" name="layout_constraintHorizontal_chainStyle">

    <enum name="spread" value="0"/>

    <enum name="spread_inside" value="1"/>

    <enum name="packed" value="2"/>

</attr>

<attr format="enum" name="layout_constraintVertical_chainStyle">

    <enum name="spread" value="0"/>

    <enum name="spread_inside" value="1"/>

    <enum name="packed" value="2"/>

</attr>

<attr format="dimension" name="layout_editor_absoluteX"/>

<attr format="dimension" name="layout_editor_absoluteY"/>

</declare-styleable>

 

解决ConstraintLayout 与ScrollView 嵌套时ScrollView 内容没有完全显示

ConstraintLayout 布局中有ScrollView 时,ScrollView 的宽高要设置为0dp 才可以正确的约束布局

<ScrollView

        android:layout_width="match_parent"

android:layout_height="0dp"

        app:layout_constraintBottom_toBottomOf="parent"

        app:layout_constraintLeft_toLeftOf="parent"

        app:layout_constraintRight_toRightOf="parent"

        app:layout_constraintTop_toBottomOf="@+id/layout_item_binding_card_indicator">

</ScrollView>

 

posted @ 2020-02-26 17:34  嘤嘤嘤123  阅读(942)  评论(0编辑  收藏  举报