Android日积月累系列之八-ConstraintLayout
1.相对定位 Relative Positioning
1.1 **RelativeLayout ** 相对定位对齐方式
先看看以前的 RelativeLayout对齐方式
<RelativeLayout
android:layout_toEndOf="定位到在某个控件的右边"
android:layout_toStartOf="定位到某个控件的左边"
android:layout_above="定位到某个控件顶部"
android:layout_below="定位到某个控件底部"
android:layout_centerInParent="定位到父容器的中间 左右上下居中"
android:layout_centerHorizontal="水平居中"
android:layout_centerVertical="垂直居中"
android:layout_alignParentBottom="true 定位到父容器底部"
android:layout_alignParentTop="true 定位到父容器顶部"
android:layout_alignParentStart="true 定位到父容器左边"
android:layout_alignParentEnd="true 定位到父容器右边"
android:layout_alignBaseline="@id/基线对齐 一般用于文本,文字大小不一,文字会底部对齐">
</RelativeLayout>
在ConstraintLayout中如何实现类似于RelativeLayout里的对齐方式呢
1.2 ConstraintLayout的相对定位
layout_constraintXXX_toYYYOf 控件的某一边,定位在某个控件的某一边
优先使用 Start,End而不是Left,Right
<ConstraintLayout
layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf>
</ConstraintLayout>
1.2.1 相对到父容器水平、垂直、左右 居中对齐
相对父容器水平居中对齐
相当于 RelativeLayout 的 android:layout_centerHorizontal="水平居中"
关键
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
是可以相对于父容器parent或者某个具体控件设置居中对齐的。
当控件A宽度和参考的目标宽度一样时这样定位,其实是左右对齐的效果,如果宽度不一致,那就是居中效果了(即不左对齐也不右对齐)。水平是这样,垂直也是这样。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
相对父容器垂直居中对齐
相当于 RelativeLayout 的 android:layout_centerVertical="垂直居中"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
定位到父容器的中间 同时水平垂直居中
android:layout_centerInParent="定位到父容器的中间 左右上下居中"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
居中对齐偏移
当控件设置为水平或得居中对齐时(相对于父容器或者某个控件都可以的)
居中对齐后,如果需要左右或者上下偏移,则可以使用以下两个属性设置。
layout_constraintHorizontal_bias layout_constraintVertical_bias
单位并不是dp也不是px、sp,取值范围是0~1,如果小于0或者大于1,会默认回0.5
当值为0时,是左边对齐,值为1时,是右边对齐,0.5是居中对齐,其他值为偏移。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
左对齐于父容器
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0"/>
右对齐于父容器
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="1"/>
0.5或者不设置偏移,则居中
<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0.5"/>
</androidx.constraintlayout.widget.ConstraintLayout>
1.2.2 定位到父容器顶部、底部、左边、右边
定位到父容器顶部
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
定位到父容器底部
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
定位到父容器左边
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
定位到父容器右边
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
1.2.3 定位到某个控件的上面、下面、左边、右边
定位到某个控件上面
关键 app:layout_constraintBottom_toTopOf="@id/btnCenter"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@id/btnCenter"/>
</androidx.constraintlayout.widget.ConstraintLayout>
定位到某个控件下面
关键: app:layout_constraintTop_toBottomOf="@id/btnCenter"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btnCenter"/>
</androidx.constraintlayout.widget.ConstraintLayout>
定位到某个控件的左边
左边 关键 app:layout_constraintEnd_toStartOf="@id/btnCenter"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn2"
app:layout_constraintEnd_toStartOf="@id/btnCenter"
app:layout_constraintTop_toBottomOf="@id/btnCenter"/>
</androidx.constraintlayout.widget.ConstraintLayout>
定位到某个控件的左边
右边 关键: app:layout_constraintStart_toEndOf="@id/btnCenter"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn2"
app:layout_constraintStart_toEndOf="@id/btnCenter"
app:layout_constraintTop_toBottomOf="@id/btnCenter"/>
</androidx.constraintlayout.widget.ConstraintLayout>
1.2.4 基线对齐
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textSize="28sp"
android:text="HelloWorld"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/tvCenter"
app:layout_constraintBaseline_toBaselineOf="@id/tvCenter"
android:textSize="18sp"
android:text="HelloWorld"/>
</androidx.constraintlayout.widget.ConstraintLayout>
2. 外边距 Relative Positioning Margins
2.1 Relative Positioning Margins
margin的属性大概以下几种
其中 android:layout_marginLeft和android:layout_marginRight是不推荐使用的,代替的是android:layout_marginStart,android:layout_marginEnd
<View
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
layout_marginBaseline/>
margin和其他布局使用方式一样,就不一一列举
app:layout_marginBaseline="100dp" 一般是用于两个TextView
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:textSize="28sp"
android:text="HelloWorld"
android:visibility="visible"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/tvCenter"
app:layout_constraintBaseline_toBaselineOf="@id/tvCenter"
android:textSize="18sp"
android:text="HelloWorld"
app:layout_marginBaseline="100dp"
android:visibility="visible"/>
</androidx.constraintlayout.widget.ConstraintLayout>
2.2 goneMargin
<View
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
layout_goneMarginBaseline>
</View>
layout_goneMarginXXX和android:layout_marginXXX一样,都有上下左右layout_goneMarginBaseline设置,有Start,End优化使用,不推荐使用Left,Right。
goneMargin一般会跟android:layout_marginXXX一起配合使用
举个粟子:控件B定位到控件A的右边,AB两个控件左右线性排列,并且控件B设计了android:layout_marginStart="100dp"
如果A控件设置 android:visibility="gone"则控件A会隐藏,并且控件B会向左移动,但是控件B仍保留有左边的100dp的间隔
如果想实现控件A显示时,他们之间有100dp的间隔,但是控件A隐藏时,间隔变小或者没有间隔,可以同时给控件B设计android:layout_marginXXX和goneMargin。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn2"
app:layout_constraintStart_toEndOf="@id/btnCenter"
app:layout_constraintTop_toBottomOf="@id/btnCenter"
android:layout_marginStart="50dp"
app:layout_goneMarginStart="0dp"
android:visibility="visible"/>
</androidx.constraintlayout.widget.ConstraintLayout>
3.圆心定位 Circular Positioning
相关的属性有
layout_constraintCircle : 参照控件id layout_constraintCircleRadius : 半径dp layout_constraintCircleAngle : 角度 (,from 0 to 360)
指定圆心,半径,角度
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="45"
app:layout_constraintCircleRadius="100dp"
android:text="btn2"/>
<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="90"
app:layout_constraintCircleRadius="100dp"
android:text="btn3"/>
<Button
android:id="@+id/btn4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="135"
app:layout_constraintCircleRadius="100dp"
android:text="btn4"/>
<Button
android:id="@+id/btn5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="180"
app:layout_constraintCircleRadius="100dp"
android:text="btn5"/>
<Button
android:id="@+id/btn6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="225"
app:layout_constraintCircleRadius="100dp"
android:text="btn6"/>
<Button
android:id="@+id/btn7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="270"
app:layout_constraintCircleRadius="100dp"
android:text="btn7"/>
<Button
android:id="@+id/btn8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="315"
app:layout_constraintCircleRadius="100dp"
android:text="btn8"/>
<Button
android:id="@+id/btn9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintCircle="@id/btnCenter"
app:layout_constraintCircleAngle="360"
app:layout_constraintCircleRadius="100dp"
android:text="btn9"/>
</androidx.constraintlayout.widget.ConstraintLayout>
4.可见性
即 View.GONE android:visibility="gone" android:visibility="visible"等
ConstraintLayout对标记为View.GONE的小部件有一个特定的处理。
消失的小部件,像往常一样,不会被显示出来,也不是布局本身的一部分(也就是说,如果被标记为消失,它们的实际尺寸不会改变)。
但在布局计算方面,GONE小部件仍然是其中的一部分,有一个重要的区别:
1.对于布局通道,它们的维度将被认为是零(基本上,它们将被解析为一个点)
2.如果它们对其他部件有限制,它们仍然会被尊重,但任何边距将被视为等于零
5. 尺寸约束
对于包含在ConstraintLayout中的小部件,不推荐使用MATCH_PARENT。类似的行为可以通过使用MATCH_CONSTRAINT来定义,并将相应的左/右或顶部/底部约束设置为“parent“
5.1设置宽度和手机一样宽
关键
android:layout_width="0dp
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn10"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="btn10"/>
</androidx.constraintlayout.widget.ConstraintLayout>
方式2:设置为 0dp
<androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/btn10"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="btn10"/>
</androidx.constraintlayout.widget.ConstraintLayout>
高度同理。
5.2 设置最小最大宽高限制
android:minWidth 设置最小宽度 android:minHeight 设置最小高度 android:maxWidth 设置最大宽度 android:maxHeight 设置最大高度
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:minHeight="100dp"
android:minWidth="100dp"
android:maxHeight="100dp"
android:maxWidth="100dp"
android:text="btn10"/>
</androidx.constraintlayout.widget.ConstraintLayout>
除了上面的四个。还可以用
layout_constraintHeight_min/max
layout_constraintWidth_min/max
app:layout_constraintHeight_min="100dp"
app:layout_constraintHeight_max="100dp"
app:layout_constraintWidth_min="100dp"
app:layout_constraintWidth_max="100dp"
<androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/btn10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHeight_min="100dp"
app:layout_constraintWidth_min="100dp"
android:text="btn10"/>
</androidx.constraintlayout.widget.ConstraintLayout>
5.3 百分比宽高
关键属性
app:layout_constraintWidth_default
app:layout_constraintHeight_default
app:layout_constraintWidth_percent
app:layout_constraintHeight_percent
设置占父容器百分比宽/高,需要先把宽/高设置为 0dp
<androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/btn10"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.5"
android:text="btn10"/>
</androidx.constraintlayout.widget.ConstraintLayout>
5.4设置宽高比例 Ratio
先设置好一个维度(宽或者高),然后设置另一个维度和它的比例
使如先设置宽度如下,高度自适应
<androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/btn11"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn10"
app:layout_constraintWidth_percent="0.5"
android:text="btn11"/>
</androidx.constraintlayout.widget.ConstraintLayout>
如果要把高度设置成和宽度一样,则需要
1.先该维度设置成0dp
2.再上设置成你想要的比例"width:height" 例如设置成 1:1 app:layout_constraintDimensionRatio="1:1"
<androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/btn11"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn10"
app:layout_constraintWidth_percent="0.5"
app:layout_constraintDimensionRatio="1:1"
android:text="btn11"/>
</androidx.constraintlayout.widget.ConstraintLayout>
所以,如果想使用比例宽高,一定会有一个维度是设置成0dp
5.4 指定是约束宽度还是高度
如果两个维度都设置为MATCH_CONSTRAINT (0dp),也可以使用ratio。在这种情况下,系统设置满足所有约束条件的最大尺寸,并保持指定的长宽比。要基于另一侧的尺寸约束某一侧,可以预先添加W”或H,分别约束宽度或高度。例如,如果一个维度被两个目标约束(例如宽度为0dp并且以父目标为中心),你可以通过在th前面添加字母W(用于约束宽度)或H(用于约束高度)来指示哪一边应该被约束
<androidx.constraintlayout.widget.ConstraintLayout>
<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"/>
</androidx.constraintlayout.widget.ConstraintLayout>
6.链条 Chains
如果一组小部件通过双向连接连接在一起,则它们被认为是一个链。
链在单个轴(水平或垂直)中提供类似组的行为。另一个轴可以被独立地约束。
链头(Chain heads):链由设置在链的第一个元素(链头)上的属性控制,同一个控件,可以同时作为水平链和垂直链的链头。
对于水平链,头部是最左边的部件,而对于垂直链,头部是最上面的部件。
6.1基本链
关键:
1.双向链接,
2.app:layout_constraintHorizontal_chainStyle
spread 平分,两边不贴边,除了各个元素占用的空间外,剩下的空间被平均分成 n+1份,n为链上控件个数
spread_inside 平分,两边贴边,除了各个元素占用的空间外,剩下的空间被平均分成 n-1份,n为链上控件个数
packed 所有控件紧贴着居中
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/A"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/blue"
android:gravity="center"
android:text="A"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintEnd_toStartOf="@id/B"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/B"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/main_red"
android:gravity="center"
android:text="B"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/A"
app:layout_constraintEnd_toStartOf="@id/C"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/C"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@color/green3"
android:gravity="center"
android:text="C"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/B"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
6.2 链与权重
链的权重,非常像LinearLayout
相关的属性有
app:layout_constraintHorizontal_weight
app:layout_constraintVertical_weight
关键,layout_width,layout_height,不能写具体的dp,指定为0dp
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/A"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="@color/blue"
android:gravity="center"
android:text="A"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintHorizontal_weight="2"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintEnd_toStartOf="@id/B"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/B"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="@color/main_red"
android:gravity="center"
android:text="B"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/A"
app:layout_constraintEnd_toStartOf="@id/C"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/C"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="@color/green3"
android:gravity="center"
android:text="C"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/B"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
7 参考线 Guideline
控件可以参考父容器,或者其他控件来定位,androidx.constraintlayout.widget.Guideline是一个View,也可以参考Guideline来定位, 参考线不会在界面上显示出来
Guideline是View的子类,但是super.setVisibility(View.GONE),即不可见,但是会有位置定位信息
public class Guideline extends View {
public Guideline(Context context) {
super(context);
super.setVisibility(8);
}
public Guideline(Context context, AttributeSet attrs) {
super(context, attrs);
super.setVisibility(8);
}
public Guideline(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
super.setVisibility(8);
}
public Guideline(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
super.setVisibility(8);
}
public void setVisibility(int visibility) {
}
public void draw(Canvas canvas) {
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
this.setMeasuredDimension(0, 0);
}
public void setGuidelineBegin(int margin) {
LayoutParams params = (LayoutParams)this.getLayoutParams();
params.guideBegin = margin;
this.setLayoutParams(params);
}
public void setGuidelineEnd(int margin) {
LayoutParams params = (LayoutParams)this.getLayoutParams();
params.guideEnd = margin;
this.setLayoutParams(params);
}
public void setGuidelinePercent(float ratio) {
LayoutParams params = (LayoutParams)this.getLayoutParams();
params.guidePercent = ratio;
this.setLayoutParams(params);
}
}
关键属性
android:orientation="horizontal" //horizontal vertical
app:layout_constraintGuide_percent= 0~1 距离父级宽度或高度 start 的百分比(小数形式)
app:layout_constraintGuide_begin="100dp" 距离父级起始位置的距离dp(horizontal左侧或vertical顶部)
app:layout_constraintGuide_end="100dp" 距离父级结束位置的距离dp(horizontal右侧或vertical底部)
<?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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="@string/button"
tools:layout_constraintTop_creator="1"
android:layout_marginStart="8dp"
tools:layout_constraintLeft_creator="1"
app:layout_constraintLeft_toLeftOf="@+id/guideline"
android:layout_marginLeft="8dp"
app:layout_constraintBaseline_toBaselineOf="@+id/button5"
app:layout_constraintRight_toLeftOf="@+id/guideline3"
android:layout_marginRight="8dp"
app:layout_constraintHorizontal_bias="0.9" />
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintLeft_toLeftOf="@+id/guideline3"
android:layout_marginLeft="8dp"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintTop_toTopOf="@+id/guideline4"
android:layout_marginTop="16dp" />
<Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button"
app:layout_constraintRight_toLeftOf="@+id/guideline2"
tools:layout_constraintTop_creator="1"
tools:layout_constraintRight_creator="1"
android:layout_marginEnd="5dp"
android:layout_marginTop="4dp"
app:layout_constraintTop_toTopOf="@+id/guideline5"
android:layout_marginRight="8dp"
android:layout_marginLeft="8dp"
app:layout_constraintLeft_toLeftOf="@+id/guideline3"
app:layout_constraintHorizontal_bias="0.5" />
<TextView
android:id="@+id/textView3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/guidelines"
android:textSize="30sp"
tools:layout_constraintTop_creator="1"
tools:layout_constraintRight_creator="1"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="16dp"
tools:layout_constraintLeft_creator="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginLeft="16dp"
app:layout_constraintHorizontal_bias="0.0"
android:layout_marginRight="16dp" />
<Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button"
tools:layout_constraintTop_creator="1"
android:layout_marginStart="8dp"
app:layout_constraintTop_toBottomOf="@+id/button6"
tools:layout_constraintLeft_creator="1"
app:layout_constraintLeft_toLeftOf="@+id/guideline"
android:layout_marginTop="0dp" />
<Button
android:id="@+id/button8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button"
tools:layout_constraintTop_creator="1"
android:layout_marginStart="10dp"
android:layout_marginTop="8dp"
tools:layout_constraintLeft_creator="1"
app:layout_constraintLeft_toLeftOf="@+id/guideline"
app:layout_constraintTop_toTopOf="@+id/guideline6" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/guideline"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.15" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/guideline2"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.85" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/guideline3"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/guideline4"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.15" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/guideline5"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.50097847" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/guideline6"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.85" />
</androidx.constraintlayout.widget.ConstraintLayout>
8. 占位符Placeholder
占位符提供了一个虚拟对象,它可以定位现有对象。
当在占位符上设置另一个视图的id(使用setContent())时,占位符实际上就变成了内容视图。如果内容视图存在于屏幕上,则将其视为离开了原始位置。
使用占位符的参数布局定位内容视图(占位符像任何其他视图一样被简单地约束在布局中)。
关键方法 public void setContentId(int id)
public class Placeholder extends View {
private int mContentId = -1;
private View mContent = null;
private int mEmptyVisibility = View.INVISIBLE;
public Placeholder(Context context) {
super(context);
init(null);
}
public Placeholder(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public Placeholder(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
public Placeholder(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
super.setVisibility(mEmptyVisibility);
mContentId = -1;
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ConstraintLayout_placeholder);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
if (attr == R.styleable.ConstraintLayout_placeholder_content) {
mContentId = a.getResourceId(attr, mContentId);
} else {
if (attr == R.styleable.ConstraintLayout_placeholder_placeholder_emptyVisibility) {
mEmptyVisibility = a.getInt(attr, mEmptyVisibility);
}
}
}
a.recycle();
}
}
/**
* Sets the visibility of placeholder when not containing objects typically gone or invisible.
* This can be important as it affects behaviour of surrounding components.
*
* @param visibility Either View.VISIBLE, View.INVISIBLE, View.GONE
*/
public void setEmptyVisibility(int visibility) {
mEmptyVisibility = visibility;
}
/**
* Returns the behaviour of a placeholder when it contains no view.
*
* @return Either View.VISIBLE, View.INVISIBLE, View.GONE. Default is INVISIBLE
*/
public int getEmptyVisibility() {
return mEmptyVisibility;
}
/**
* Returns the content view
* @return {@code null} if no content is set, otherwise the content view
*/
public View getContent() {
return mContent;
}
/**
* Sets the content view id
*
* @param id the id of the content view we want to place in the Placeholder
*/
public void setContentId(int id) {
if (mContentId == id) {
return;
}
if (mContent != null) {
mContent.setVisibility(VISIBLE); // ???
ConstraintLayout.LayoutParams layoutParamsContent = (ConstraintLayout.LayoutParams) mContent
.getLayoutParams();
layoutParamsContent.isInPlaceholder = false;
mContent = null;
}
mContentId = id;
if (id != ConstraintLayout.LayoutParams.UNSET) {
View v = ((View) getParent()).findViewById(id);
if (v != null) {
v.setVisibility(GONE);
}
}
}
}
8 约束Helper ConstraintHelper
Guideline,Barrier是ConstraintHelper的直接子类,ConstraintHelper是View的子类,ConstraintHelper的子类有以下几个
Barrier (androidx.constraintlayout.widget)
Group (androidx.constraintlayout.widget)
Layer (androidx.constraintlayout.helper.widget)
MotionHelper (androidx.constraintlayout.motion.widget)
VirtualLayout (androidx.constraintlayout.widget)
ConstraintHelper公有的属性为,常用的是constraint_referenced_tags
app:constraint_referenced_tags="1,1,1"
app:constraint_referenced_ids="A,B"
8.1 屏障 Barrier
可以参考这里的例子 ConstraintLayout barriers
大概要解决的问题是,之前的定位,都是某一个控件相对于某一个控件,现在可以设置某一个控件,相对于某几个控件的某一边来定位,
以解决某些情况下,有时控件A的长度比控件B的长度长,有时则反之,如果能相对于几个控件最长的一边来定位,就不会发生交错的问题。
会交错的示例
TextView C位于TextView A,B的右边,TextViewA文本变化时,TextViewC跟随移动,但是TextViewB文本变长后,TextView B和C的文本就交错在一起了。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/A"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="@color/blue"
android:gravity="center"
android:text="AAAAAAAA"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/B"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="@color/main_red"
android:gravity="center"
android:text="BBBBBBBBBBB"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/A"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/C"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/green3"
android:gravity="center"
android:text="Cccccc\ncccc\nccc\nccc\nccc"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/A"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
使用 androidx.constraintlayout.widget.Barrier优化
Barrier也是和Guideline一样super.setVisibility(View.GONE);
关键属性
app:barrierDirection="end" 屏障位于这一组View的哪一边 start/end/top/bottom
app:constraint_referenced_ids="A,B" 组成屏障的ids
public class Barrier extends ConstraintHelper {
public Barrier(Context context) {
super(context);
super.setVisibility(View.GONE);
}
public Barrier(Context context, AttributeSet attrs) {
super(context, attrs);
super.setVisibility(View.GONE);
}
public Barrier(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
super.setVisibility(View.GONE);
}
}
优化后的xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/A"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="@color/blue"
android:gravity="center"
android:text="AAAAAAAA"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/B"
android:layout_width="0dp"
android:layout_height="80dp"
android:background="@color/main_red"
android:gravity="center"
android:text="BBBBBBBBBBB"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/A"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="A,B"/>
<TextView
android:id="@+id/C"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/green3"
android:gravity="center"
android:text="Cccccc\ncccc\nccc\nccc\nccc"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/barrier1"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
8.2 组 Group
看Group代码可知,Group作用是把几个控件组成一组,统一设计这一组控件的Visibility和elevation
public class Group extends ConstraintHelper {
public Group(Context context) {
super(context);
}
public Group(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Group(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* @suppress
* @param attrs
*/
protected void init(AttributeSet attrs) {
super.init(attrs);
mUseViewMeasure = false;
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
applyLayoutFeatures();
}
@Override
public void setVisibility(int Visibility) {
super.setVisibility(visibility);
applyLayoutFeatures();
}
@Override
public void setElevation(float elevation) {
super.setElevation(elevation);
applyLayoutFeatures();
}
/**
* @suppress
* @param container
*/
@Override
protected void applyLayoutFeaturesInConstraintSet(ConstraintLayout container) {
applyLayoutFeatures(container);
}
/**
* @suppress
* @param container
*/
@Override
public void updatePostLayout(ConstraintLayout container) {
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) getLayoutParams();
params.widget.setWidth(0);
params.widget.setHeight(0);
}
}
可以在代码里,也可以在xml里设置 Visibility和elevation
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/A"
android:layout_width="0dp"
android:layout_height="80dp"
android:gravity="center"
android:text="AAAAAAAA"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/B"
android:layout_width="0dp"
android:layout_height="80dp"
android:gravity="center"
android:text="BBBBBBBBBBB"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/A"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/C"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Cccccc\ncccc\nccc\nccc\nccc"
android:textColor="@color/black"
android:textSize="25sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/barrier1"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Group
android:id="@+id/barrier1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="A,B,C"
android:elevation="100dp"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>
8.3 Flow
flow是ConstraintHelper的间接子类,是VirtualLayout的直接子类。
VirtualLayouts和ViewGroups之间的主要区别是:
1.VirtualLayout保持层次结构平坦
2.因此,其他视图不仅可以引用/约束到VirtualLayout,还可以引用/约束到VirtualLayout所布局的视图
3.VirtualLayout允许动态修改行为(例如,对于Flow,改变方向)
关键属性 flow_wrapMode
8.3.1 flow_wrapMode = "none"
这将在引用的小部件上创建一个水平或垂直的链。这是Flow的默认行为。该模式下允许的XML属性:
- flow_horizontalStyle = "spread|spread_inside|packed"
- flow_verticalStyle = "spread|spread_inside|packed"
- flow_horizontalBias = "float 设置水平百分偏移"
- flow_verticalBias = "float 垂直同上"
- flow_horizontalGap = "dimension 设置水平间隔"
- flow_verticalGap = "dimension 设置垂直间隔"
- flow_horizontalAlign = "start|end 用于链上控件宽高不一的情况下,如何对齐"
- flow_verticalAlign = "top|bottom|center|baseline 同上"
- android:orientation="horizontal | vertical 决定是水平还是垂直方向"
- app:flow_maxElementsWrap="3换行时,指定每一行最多多少个控件"
先定义整条链的方向,宽/高,flow_wrapMode
<androidx.constraintlayout.helper.widget.Flow
//设置宽高定位信息 设置占满宽/高,否则flow_horizontalStyle可能不生效。
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:flow_horizontalStyle="packed"
android:orientation="horizontal"
app:flow_wrapMode="none"
app:constraint_referenced_ids="tv1,tv2,tv3,tv4,tv5,tv6"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv1"
android:layout_width="100dp"
android:layout_height="50dp"
android:text="1"
android:background="@color/green3"/>
<TextView
android:id="@+id/tv2"
android:layout_width="110dp"
android:layout_height="50dp"
android:text="2"
android:background="@color/main_gray"/>
<TextView
android:id="@+id/tv3"
android:layout_width="120dp"
android:layout_height="50dp"
android:text="3"
android:background="@color/orange_50"/>
<TextView
android:id="@+id/tv4"
android:layout_width="130dp"
android:layout_height="50dp"
android:text="4"
android:background="@color/blue"/>
<TextView
android:id="@+id/tv5"
android:layout_width="140dp"
android:layout_height="50dp"
android:text="5"
android:background="@color/black"/>
<TextView
android:id="@+id/tv6"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="6"
android:background="@color/main_red"/>
<androidx.constraintlayout.helper.widget.Flow
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:flow_horizontalStyle="packed"
android:orientation="vertical"
app:flow_wrapMode="none"
app:flow_horizontalGap="5dp"
app:flow_horizontalAlign="start"
app:constraint_referenced_ids="tv1,tv2,tv3,tv4,tv5,tv6"/>
</androidx.constraintlayout.widget.ConstraintLayout>
8.3.2 flow_wrapMode = "chain"
在创建链方面类似于不换行,但是如果引用的小部件不适合水平或垂直维度(取决于选择的方向),它们将换行到下一行/列。XML属性与wrap_none中的相同,只是对第一个链添加了指定链样式和链偏差的属性。这样,就可以在第一个链和最终创建的其他链之间指定不同的链行为
即一行容不下,会自动换到下一行。
- flow_firstHorizontalStyle = "spread|spread_inside|packed 整体由flow_horizontalStyle/flow_verticalStyle控制,精细控制控制水平第一行的平分方式.相似的属性有 flow_lastVerticalStyle/flow_lastHorizontalStyle"
- flow_firstVerticalStyle = "spread|spread_inside|packed同上"
- flow_firstHorizontalBias = "float 第一行水平偏移"
- flow_firstVerticalBias = "float 第一行垂直偏移"‘
8.3.3 flow_wrapMode = "aligned"
与WRAP_CHAIN相同的XML属性,不同的是元素将以一组行和列而不是链的形式布局。因此,指定链样式和偏差的属性将不会被应用。
8.4 CircularFlow
一个圆形的约速链,类似于圆心定位 Circular Positioning
关键属性
app:circularflow_viewCenter="tv1 这一个id也要包含在constraint_referenced_ids里"
app:circularflow_angles="45,90,135,180,225,270,315,360,链上除了circularflow_viewCenter外,每一个元素的角度"
app:circularflow_radiusInDP="100,100,100,100,100,100,100,100 半径"
app:constraint_referenced_ids="tv1,tv2,tv3,tv4,tv5,tv6,tv7,tv8,tv9 组成链的ids,包含中心和圆点"
也可以在代码里设置圆心半径等信息
public class CircularFlow extends VirtualLayout {
private static final String TAG = "CircularFlow";
ConstraintLayout mContainer;
int mViewCenter;
private static int DEFAULT_RADIUS = 0;
private static float DEFAULT_ANGLE = 0F;
/**
* @suppress
*/
private float[] mAngles;
/**
* @suppress
*/
private int[] mRadius;
/**
* @suppress
*/
private int mCountRadius;
/**
* @suppress
*/
private int mCountAngle;
/**
* @suppress
*/
private String mReferenceAngles;
/**
* @suppress
*/
private String mReferenceRadius;
/**
* @suppress
*/
private Float mReferenceDefaultAngle;
/**
* @suppress
*/
private Integer mReferenceDefaultRadius;
public CircularFlow(Context context) {
super(context);
}
public CircularFlow(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CircularFlow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public int[] getRadius() {
return Arrays.copyOf(mRadius, mCountRadius);
}
public float[] getAngles() {
return Arrays.copyOf(mAngles, mCountAngle);
}
/**
* Set default Angle for CircularFlow.
*
* @param angle
* @return
*/
public void setDefaultAngle(float angle) {
DEFAULT_ANGLE = angle;
}
/**
* Set default Radius for CircularFlow.
*
* @param radius
* @return
*/
public void setDefaultRadius(int radius) {
DEFAULT_RADIUS = radius;
}
/**
* @suppress
*/
private float[] removeAngle(float[] angles, int index) {
if (angles == null
|| index < 0
|| index >= mCountAngle) {
return angles;
}
return removeElementFromArray(angles, index);
}
/**
* @suppress
*/
private int[] removeRadius(int[] radius, int index) {
if (radius == null
|| index < 0
|| index >= mCountRadius) {
return radius;
}
return removeElementFromArray(radius, index);
}
/**
* @suppress
*/
private void setAngles(String idList) {
if (idList == null) {
return;
}
int begin = 0;
mCountAngle = 0;
while (true) {
int end = idList.indexOf(',', begin);
if (end == -1) {
addAngle(idList.substring(begin).trim());
break;
}
addAngle(idList.substring(begin, end).trim());
begin = end + 1;
}
}
/**
* @suppress
*/
private void setRadius(String idList) {
if (idList == null) {
return;
}
int begin = 0;
mCountRadius = 0;
while (true) {
int end = idList.indexOf(',', begin);
if (end == -1) {
addRadius(idList.substring(begin).trim());
break;
}
addRadius(idList.substring(begin, end).trim());
begin = end + 1;
}
}
/**
* @suppress
*/
private void addAngle(String angleString) {
if (angleString == null || angleString.length() == 0) {
return;
}
if (myContext == null) {
return;
}
if (mAngles == null) {
return;
}
if (mCountAngle + 1 > mAngles.length) {
mAngles = Arrays.copyOf(mAngles, mAngles.length + 1);
}
mAngles[mCountAngle] = Integer.parseInt(angleString);
mCountAngle++;
}
/**
* @suppress
*/
private void addRadius(String radiusString) {
if (radiusString == null || radiusString.length() == 0) {
return;
}
if (myContext == null) {
return;
}
if (mRadius == null) {
return;
}
if (mCountRadius + 1 > mRadius.length) {
mRadius = Arrays.copyOf(mRadius, mRadius.length + 1);
}
mRadius[mCountRadius] = (int) (Integer.parseInt(radiusString) * myContext.getResources().getDisplayMetrics().density);
mCountRadius++;
}
}
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv1"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="1"
android:background="@color/green3"/>
<TextView
android:id="@+id/tv2"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="2"
android:background="@color/main_gray"/>
<TextView
android:id="@+id/tv3"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="3"
android:background="@color/orange_50"/>
<TextView
android:id="@+id/tv4"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="4"
android:background="@color/blue"/>
<TextView
android:id="@+id/tv5"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="5"
android:background="@color/black"/>
<TextView
android:id="@+id/tv6"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="6"
android:background="@color/main_red"/>
<TextView
android:id="@+id/tv7"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="7"
android:background="@color/main_red"/>
<TextView
android:id="@+id/tv8"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="8"
android:background="@color/main_red"/>
<TextView
android:id="@+id/tv9"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="9"
android:background="@color/main_red"/>
<androidx.constraintlayout.helper.widget.CircularFlow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:circularflow_viewCenter="tv1"
app:circularflow_angles="45,90,135,180,225,270,315,360"
app:circularflow_radiusInDP="100,100,100,100,100,100,100,100"
app:constraint_referenced_ids="tv1,tv2,tv3,tv4,tv5,tv6,tv7,tv8,tv9"/>
</androidx.constraintlayout.widget.ConstraintLayout>
9.ConstraintSet
一个示例活动,展示使用ConstraintSet的情况。它从一个单一的ConstraintLayout开始,然后使用ConstraintSet.applyTo(ConstraintLayout)转换到一个不同的ConstraintLayout。
从一个布局切换到另外一个布局,并且有切换动画
public class ConstraintSetExampleActivity extends AppCompatActivity {
private static final String SHOW_BIG_IMAGE = "showBigImage";
/**
* Whether to show an enlarged image
*/
private boolean mShowBigImage = false;
/**
* The ConstraintLayout that any changes are applied to.
*/
private ConstraintLayout mRootLayout;
/**
* The ConstraintSet to use for the normal initial state
*/
private ConstraintSet mConstraintSetNormal = new ConstraintSet();
/**
* ConstraintSet to be applied on the normal ConstraintLayout to make the Image bigger.
*/
private ConstraintSet mConstraintSetBig = new ConstraintSet();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constraintset_example_main);
mRootLayout = (ConstraintLayout) findViewById(R.id.activity_constraintset_example);
// Note that this can also be achieved by calling
// `mConstraintSetNormal.load(this, R.layout.constraintset_example_main);`
// Since we already have an inflated ConstraintLayout in `mRootLayout`, clone() is
// faster and considered the best practice.
mConstraintSetNormal.clone(mRootLayout);
// Load the constraints from the layout where ImageView is enlarged.
mConstraintSetBig.load(this, R.layout.constraintset_example_big);
if (savedInstanceState != null) {
boolean previous = savedInstanceState.getBoolean(SHOW_BIG_IMAGE);
if (previous != mShowBigImage) {
mShowBigImage = previous;
applyConfig();
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SHOW_BIG_IMAGE, mShowBigImage);
}
// Method called when the ImageView within R.layout.constraintset_example_main
// is clicked.
public void toggleMode(View v) {
TransitionManager.beginDelayedTransition(mRootLayout);
mShowBigImage = !mShowBigImage;
applyConfig();
}
private void applyConfig() {
if (mShowBigImage) {
mConstraintSetBig.applyTo(mRootLayout);
} else {
mConstraintSetNormal.applyTo(mRootLayout);
}
}
}