ConstraintLayout 1.1.0(测试版)中链中的边距如何工作

How do margins in chains work in ConstraintLayout 1.1.0 (beta)

自从切换到 ConstraintLayout 版本 1.1.0-beta4 后,我的一些布局被炸毁了。在进行任何更改之前,我想更好地了解边距在 ConstraintLayout 链中的工作原理。下面,我将 ConstraintLayout 版本 1.0.2 中的布局与版本 1.1.0-beta4 进行了比较,但我认为问题首先出现在 1.1.0-beta2 中。

我的目标是让一些文本视图在屏幕上伸展,在第一个和第二个文本视图以及第二个和第三个文本视图之间留有间隙。背景应显示在这些边距中。为此,我创建了一个水平链并指定了从左侧文本视图到中心文本视图的结束边距以及从中心文本视图到右侧文本视图的结束边距。横链样式为spread_inside

示例 1 - 使用 ConstraintLayout 版本 1.0.2

这是版本 1.0.2 中的样子,也是我所期望的。

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@android:color/holo_blue_light">

    <TextView
        android:id="@+id/tvLeft"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginEnd="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="Text1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/tvCenter"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/tvCenter"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginEnd="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="Text2"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/tvRight"
        app:layout_constraintStart_toEndOf="@+id/tvLeft"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/tvRight"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="Text3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/tvCenter"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>

示例 2 - 使用 ConstraintLayout 版本 1.1.0-beta4

此布局在 ConstraintLayout 的 1.1.0-beta4 版中看起来如下所示。请注意边距已经消失。我希望这看起来应该与示例 1 相同,但事实并非如此。

示例 3 - 使用具有开始边距的 ConstraintLayout 版本 1.1.0-beta4

如果我采用相同的布局并简单地向右侧文本视图 (tvRight) 添加 8dp 的起始边距,我的边距不仅会重新出现在中间文本视图和右侧文本视图之间,还会在左边和中间的文本视图之间,虽然我没有改变那里的边距。

这不仅仅是突然兑现之前设定的利润率。如果我将最右侧文本视图的起始页边距设置为“48dp”,那么 48dp 边距也会出现在左侧和中心文本视图之间。

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@android:color/holo_blue_light">

<!-- TextViews tvLeft & tvRight not shown but are the same as above.-->

<TextView
    android:id="@+id/tvRight"
    android:layout_width="0dp"
    android:layout_height="35dp"
    android:layout_marginStart="48dp"
    android:background="@android:color/white"
    android:gravity="center"
    android:text="Text3"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@+id/tvCenter"
    app:layout_constraintTop_toTopOf="parent"
    tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>  

所以,我的问题是,"Why am I seeing these results?" ConstraintLayout 链,尤其是 spread_inside 链如何处理边距?链边距的处理方式是否发生了变化,或者我遗漏了什么?我正在寻找解释或参考一些解释所有这些的文档。

在 constraintLayout 中,chain-style 的工作方式如下

1.Spread: 浏览量分布均匀。例如。 app:layout_constraintHorizontal_chainStyle=”传播” app:layout_constraintVertical_chainStyle=“传播”

2.Spread里面: 第一个和最后一个视图分别贴在链条两端的约束条件上,其余均匀分布。例如。 app:layout_constraintHorizontal_chainStyle=”spread_inside” app:layout_constraintVertical_chainStyle=”spread_inside”

3.Packed: 视图打包在一起(在考虑边距之后)。然后,您可以通过更改链的头部视图偏差来调整整个链的偏差(left/right 或 up/down)。例如。 app:layout_constraintHorizontal_chainStyle="打包" app:layout_constraintVertical_chainStyle=“打包”

4.Weighted: 当链设置为展开或内部展开时,您可以通过将一个或多个视图设置为来填充剩余的 space “匹配约束”(0dp)。默认情况下,space 在设置为 "match constraints," 的每个视图之间均匀分布,但您可以使用 thelayout_constraintHorizontal_weight 和 layout_constraintVertical_weight 属性为每个视图分配重要性权重。如果您熟悉线性布局中的 layout_weight,则其工作方式相同。所以权重值最高的视图获得最多的space;具有相同权重的视图获得相同数量的 space.

更多信息可以参考下图

我找不到对这个确切问题给出权威答案的文档。然而,在 the API documentation for ConstraintLayout:

中有一些关于边距的讨论

If side margins are set, they will be applied to the corresponding constraints (if they exist)

在链的特定实例中,每个视图之间都有双向约束。也就是说,不仅视图 A 的结束被限制为视图 B 的开始,而且视图 B 的开始也被限制为视图 A 的结束。

在您发布的布局中,视图 A 有一个结束约束和一个结束边距,但视图 B 有一个没有起始边距的开始约束。据我所知,这意味着您的布局中存在相互冲突的规则(视图 A 希望与视图 B 相距 8dp,但视图 B 希望与视图 A 相距 0dp)。也许不同版本的 ConstraintLayout 库有不同的策略来 (a) 确定这是否算作冲突,以及 (b) 如果算作冲突则解决冲突。

通过实验,我发现边距在不同 ConstraintLayout 库版本的链中工作:

版本 1.0.2

链中每个视图的边距不依赖于或影响链中的其他视图。这对行为有(至少)两个明显的影响。首先,向一个视图添加边距会将另一个视图推开该数量,而不管该视图的边距如何。其次,为一个视图添加边距不会影响链中更远的视图的边距(例如,在您的第一个视图上放置 8dp 的结束边距本身不会导致价值 8dp 的 space 出现在您的第二个和第三个视图之间) .

版本 1.1.0-beta4

链中每个视图的边距都取决于并影响链中的其他视图。同样,这对行为有两个明显的影响。首先,向一个视图添加边距不会将另一个视图推开,除非它也有相同数量的边距*。其次,在链的第一个和第二个视图之间添加边距也会影响链的第二个和第三个视图之间的间距**。

*: 似乎 1.1.0-beta4 只允许开始边距将视图分开,而只允许结束边距没有效果。无论如何,我建议匹配页边距。

**:我怀疑这是因为链试图平均分配"space"。视图 A 和 B 之间的边距产生了一个间隙,并且由于链希望强制执行一致的间距,因此它在视图 B 和 C 之间增加了一个类似的间隙。

示例:

精简版式,这里的版式与您的原始版式一样,边距略有变化。我保留了所有其他属性不变。

<android.support.constraint.ConstraintLayout>

    <TextView
        android:layout_marginEnd="8dp"/>

    <TextView
        android:layout_marginStart="8dp"/>

    <TextView/>

</android.support.constraint.ConstraintLayout>

v1.0.2:

v1.1.0-beta4:

这应该可以说明库版本之间的两个差异。同样,我完全找不到解释这一切的官方文档,但仅基于实验似乎是真的。

根据 Ben P. 的回答,我确定了以下有关 ConstraintLayout 链中边距的信息。此信息适用于 ConstraintLayout 版本 1.1.0-beta4。

一般观察

  1. 在一个链中,所有起始页边距 (android:layout_marginStart) 都被接受。这意味着视图之间的间距不会小于指定的起始页边距。但是,间距可能更大,如下所述。

  2. 页边距 (android:layout_marginEnd) 没有相关性,似乎被忽略了。这不适用于链末端视图的末端边距,但仅适用于视图 cross-linked 以创建链的内部边距。

  3. 当链在其约束内居中时,链在链头的起始边距和链尾的结束边距之间居中。

在下面的示例中,视图 "A"、"D" 和 "G" 被限制在 parent 开始。视图 "C"、"F" 和 "I" 被限制在 parent 端。

链样式:packed

如果链样式是 "packed," 所有视图都放置 end-to-end 由指定的起始页边距分隔。视图之间的间距可以根据起始页边距的定义方式而变化。在下图中,视图的宽度为 match_constraints,边距按指示设置。

如果视图的宽度设置为 match_constraints 以外的值,视图仍会按指定的边距打包,但链在链头的起始边距和链的结束边距之间居中链条的尾巴。

我得出这种解释,而不是考虑将结束边距附加到端视图,因为 Android Studio 设计师有相同的解释:

链样式:spread

在"spread"链样式中,所有视图分布在开始和结束约束之间,使得每个视图前后的space相同并且等于定义的最大开始边距。如果每个view的宽度都是match_constraints,那么所有的view默认都是一样的宽度。

链样式:spread_inside

链的 spread_inside 样式将采用链的第一个视图并将其锚定到其起始约束,同时遵守其起始边距。端视图将锚定到其端约束,同时遵守其端边距。内部视图将在视图之间以相等的间距分布,如 spread 链。


下面是设置了不同边距的相同版式。视图 "F" 和 "I" 的起始边距设置为 8dp,但差距已扩大到 16dp。视图 "G"、"H" 和 "I" 都是等宽的,尽管它们看起来并非如此。

此布局的 XML 显示在此 post 的末尾。

有趣但不重要:如果视图的宽度为 match_constraints 且所有边距均为零,则无法区分不同的链类型。

以上信息也适用于垂直链。将 android:layout_marginStart 替换为 android:layout_marginTop,将 android:layout_marginEnd 替换为 android:layout_marginBottom

布局

<android.support.constraint.ConstraintLayout 
    android:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_light">

    <TextView
        android:id="@+id/heading1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="packed, match_constraints"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textSize="16sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/textA"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="A"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toStartOf="@+id/textB"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/heading1"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        app:layout_constraintEnd_toStartOf="@id/textB"
        app:layout_constraintTop_toTopOf="@id/textB" />

    <TextView
        android:id="@+id/textB"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="B"
        android:textColor="@android:color/white"
        app:layout_constraintEnd_toStartOf="@+id/textC"
        app:layout_constraintStart_toEndOf="@+id/textA"
        app:layout_constraintTop_toTopOf="@+id/textA"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view16dpOnC"
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#fffb00"
        app:layout_constraintEnd_toStartOf="@id/textC"
        app:layout_constraintTop_toTopOf="@+id/textC" />

    <TextView
        android:id="@+id/textC"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="16dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="C"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textB"
        app:layout_constraintTop_toTopOf="@+id/textA"
        tools:ignore="HardcodedText" />

    <TextView
        android:id="@+id/heading2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="spread, match_constraints"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textSize="16sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textA"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#00ff19"
        app:layout_constraintEnd_toStartOf="@id/textD"
        app:layout_constraintTop_toTopOf="@id/textD" />

    <TextView
        android:id="@+id/textD"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="D"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toStartOf="@+id/textE"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/heading2"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#fffb00"
        app:layout_constraintEnd_toStartOf="@id/textE"
        app:layout_constraintTop_toTopOf="@id/textE" />

    <TextView
        android:id="@+id/textE"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="16dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="E"
        android:textColor="@android:color/white"
        app:layout_constraintEnd_toStartOf="@+id/textF"
        app:layout_constraintStart_toEndOf="@+id/textD"
        app:layout_constraintTop_toTopOf="@+id/textD"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#003cff"
        app:layout_constraintStart_toEndOf="@id/textE"
        app:layout_constraintTop_toTopOf="@+id/textE" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        app:layout_constraintEnd_toStartOf="@id/textF"
        app:layout_constraintTop_toTopOf="@id/textF" />

    <TextView
        android:id="@+id/textF"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="F"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textE"
        app:layout_constraintTop_toTopOf="@+id/textD"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#00ff19"
        app:layout_constraintStart_toEndOf="@id/textF"
        app:layout_constraintTop_toTopOf="@id/textF" />

    <TextView
        android:id="@+id/heading3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="spread_inside, match_constraints"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textSize="16sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textD"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#003cff"
        app:layout_constraintEnd_toStartOf="@id/textG"
        app:layout_constraintTop_toTopOf="@+id/textG" />

    <TextView
        android:id="@+id/textG"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="G"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toStartOf="@+id/textH"
        app:layout_constraintHorizontal_chainStyle="spread_inside"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/heading3"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="16dp"
        android:layout_height="35dp"
        android:background="#fffb00"
        app:layout_constraintEnd_toStartOf="@id/textH"
        app:layout_constraintTop_toTopOf="@id/textH" />

    <TextView
        android:id="@+id/textH"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="16dp"
        android:background="@android:color/darker_gray"
        android:gravity="center"
        android:text="H"
        android:textColor="@android:color/white"
        app:layout_constraintEnd_toStartOf="@+id/textI"
        app:layout_constraintStart_toEndOf="@+id/textG"
        app:layout_constraintTop_toTopOf="@+id/textG"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#003cff"
        app:layout_constraintStart_toEndOf="@id/textH"
        app:layout_constraintTop_toTopOf="@id/textH" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        app:layout_constraintEnd_toStartOf="@id/textI"
        app:layout_constraintTop_toTopOf="@id/textI" />

    <TextView
        android:id="@+id/textI"
        android:layout_width="0dp"
        android:layout_height="35dp"
        android:layout_marginStart="8dp"
        android:background="@android:color/white"
        android:gravity="center"
        android:text="I"
        android:textColor="@android:color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/textH"
        app:layout_constraintTop_toTopOf="@+id/textG"
        tools:ignore="HardcodedText" />

    <View
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:background="#ff00cc"
        android:visibility="gone"
        app:layout_constraintEnd_toStartOf="@id/textC"
        app:layout_constraintTop_toTopOf="@id/textC" />

    <View
        android:id="@+id/view8dp"
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:background="#ff00cc"
        app:layout_constraintBottom_toTopOf="@id/view8dpGap"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textG"
        app:layout_constraintVertical_bias="0.100000024"
        app:layout_constraintVertical_chainStyle="packed" />

    <TextView
        android:id="@+id/text8dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="8dp start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view8dp"
        app:layout_constraintStart_toEndOf="@id/view8dp"
        app:layout_constraintTop_toTopOf="@+id/view8dp"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view8dpGap"
        android:layout_width="8dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:background="#003cff"
        app:layout_constraintBottom_toTopOf="@+id/view16dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view8dp" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:text="8dp gap not defined by start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view8dpGap"
        app:layout_constraintStart_toEndOf="@+id/view8dpGap"
        app:layout_constraintTop_toTopOf="@+id/view8dpGap"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view16dp"
        android:layout_width="17dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:background="#fffb00"
        app:layout_constraintBottom_toTopOf="@+id/view16dpGap"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view8dpGap" />

    <TextView
        android:id="@+id/text16dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:text="16dp start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view16dp"
        app:layout_constraintStart_toEndOf="@+id/view16dp"
        app:layout_constraintTop_toTopOf="@+id/view16dp"
        tools:ignore="HardcodedText" />

    <View
        android:id="@+id/view16dpGap"
        android:layout_width="17dp"
        android:layout_height="35dp"
        android:layout_marginStart="24dp"
        android:layout_marginTop="8dp"
        android:background="#00ff19"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view16dp" />

    <TextView
        android:id="@+id/text16dpGap"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:text="16dp gap not defined by start margin"
        app:layout_constraintBottom_toBottomOf="@+id/view16dpGap"
        app:layout_constraintStart_toEndOf="@+id/view16dpGap"
        app:layout_constraintTop_toTopOf="@+id/view16dpGap"
        tools:ignore="HardcodedText" />

</android.support.constraint.ConstraintLayout>