Android日积月累系列之八-ConstraintLayout-约束布局

1,072 阅读12分钟

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_marginLeftandroid: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_marginXXXgoneMargin

 <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作用是把几个控件组成一组,统一设计这一组控件的Visibilityelevation

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里设置 Visibilityelevation

<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);
        }
    }
}