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界面
下图是ConstraintLayout的子view的参数界面
使用
导入
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
用一张图来说明
在layout design中
通过拖动view的小圆点到其他view的某个原点来设置约束。
layout_constraintXXX_bias
当同时设置水平/垂直的两边时,这个参数才有效。
如果没有设置这两个参数时,表示的是0.5,
l layout_constraintHorizontal_bias
表示view在左右限制的范围内的位置,0表示view紧挨左边,1表示紧挨右边。
l layout_constraintVertical_bias
和上边的类似。0表示view紧挨上边,1表示紧挨下边。
在layout design中的设置:
子view的几种大小
中间的那个正方形区域,它是用来控制控件大小的。一共有三种模式可选,每种模式都使用了一种不同的符号表示,点击符号即可进行切换。
layout_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的影响。
layout_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
这三种值尽量在一个控件中只是用一种,
layout_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中支持自动添加约束的功能,可以极大程度上简化那些繁琐的操作。
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中心点的位置。贴张官网图。
即表示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角度
链Chain
链在水平或者垂直方向提供一组类似行为。如图所示可以理解为横向链。这里需要了解一点,A与parent的左边缘约束,B与parent的右边边缘约束,A右边和B左边之间相互约束,才能使用一条链。多个元素之间也是如此,最左最右与parent约束,元素之间边相互约束。不然下面的链式永远无法生效。
横向链最左边第一个控件,垂直链最顶边第一个控件称为链头,可以通过下面两个属性链头统一定制链的样式。
l layout_constraintHorizontal_chainStyle 水平方向链式
l layout_constraintVertical_chainStyle 垂直方向链式
它两的值可以是
l spread:视图是均匀分布的(考虑到边距margins 后)。 这是默认值。
l spread_inside:第一个和最后一个视图附加到链的每一端的约束上,其余视图均匀分布。
l packed:视图打包在一起(在考虑了边距之后)。可以通过修改chain head view的偏移bias,来改变packed views的位置。
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的方式:
Barrier
Barrier有点类似Guideline,但Barrier会根据所有引用的控件尺寸的变化重新定位。例如经典的登录界面,右边的EditText总是希望与左右所有TextView的最长边缘靠齐。 如果两个TextView其中一个变得更长,EditText的位置都会跟这变化,这比使用RelativeLayout灵活很多。
l app:barrierDirection所引用控件对齐的位置,可设置的值有:bottom、end、left、right、start、top.
l constraint_referenced_ids为所引用的控件,例如下边例子的tvPhone,tvPasswrod。
代码:
<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默认可见时,是这样的。
设置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都被隐藏了。
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>