51

2020 年 7 月更新:

在下面的答案中添加了信息,以更详细地解释为什么/做constrainedWidth/Height什么以及何时适用于使用它们。

2018 年 7 月更新:

如果您正在使用ConstraintLayout 1.1.0,则要使用的正确属性将app:layout_constrainedWidth="true"代替旧的app:layout_constraintWidth_default="wrap"(和高度对应物)。请参阅更新的答案。

2017 年 11 月更新:

如果您使用的是ConstraintLayout 1.0.0稳定版(或更高版本)(此时为 1.0.2),请参阅更新后的答案以获得更简单的解决方案,而无需嵌套布局。

原始问题:

使用ConstraintLayouts-Beta3于 2016 年 11 月 3 日发布。(来源

我正在尝试执行以下操作:

|                                        |
|<-[TextView]<->[ImageView] -----------> |
|                                        |

我通过这样的布局实现了这一点:

  <TextView
      android:id="@+id/textView"
      android:layout_height="wrap_content"
      android:layout_width="wrap_content"

      app:layout_constraintHorizontal_chainStyle="packed"

      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintRight_toLeftOf="@+id/caret"
      app:layout_constraintHorizontal_bias="0.0"

      android:text="Some Text"
      android:textAlignment="viewStart"
      android:gravity="start" />

  <ImageView
      android:id="@+id/caret"
      android:layout_width="wrap_content"
      android:layout_height="8dp"
      app:layout_constraintDimensionRatio="1:1"

      app:layout_constraintLeft_toRightOf="@+id/textView"
      app:layout_constraintRight_toRightOf="parent"

      app:layout_constraintTop_toTopOf="@+id/textView"
      app:layout_constraintBottom_toBottomOf="@+id/textView"


      app:layout_constraintHorizontal_bias="0.0"

      android:contentDescription=""
      app:srcCompat="@drawable/ic_selection"
      android:layout_marginStart="8dp"/>

这看起来不错,但是当文本长于可用空间时……</p>

|                                        |
|<-[TextView Larger Than The Space Avail]|
|                                        |

文本视图具有指定这些的样式:

<item name="android:lines">1</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>

所以它应该可以工作,但我不确定我需要什么约束才能让图像滑动到右边然后在那里让文本视图明白没有更多空间。

我错过了什么?

注意:如果我将 textview 的宽度设置为 0dp,它可以工作,但是图像总是在右边(水平偏差似乎被忽略了)

注意2:我也用beta2试过这个,事实上,看起来Beta3在可视化编辑器中有一个bug

更新:我试图在 Xcode/AutoLayout 中复制它:

这是短文本的外观

短文本

现在同样的布局,我只是在文本视图中输入一个长文本……</p>

长文本

如您所见,图像视图的轨迹(右)约束说:您是距右边距8 点或更多点。

它也固定在标签 (textView) 的左侧。

根据我刚刚从 Twitter 中了解到的情况,目前这在 Android 的 ConstraintLayout 上可能是不可能的: Source

4

5 回答 5

62

2020 年 7 月更新:受约束的宽度/高度有什么作用?

constrainedWidth/Height很多人一直问我,当设置为 true(默认为 false)时,它到底做了什么。我终于有了一个答案(来自谷歌员工),所以为了代替澄清所有来这篇文章的人一直有的疑虑,这就是我收集的内容(有些是我的话,有些是来自原始谷歌问题报价的直接引用。

ConstraintLayout需要确定所涉及的每个视图的维度,并且根据所述视图的约束方式,它必须执行不同的计算。

给出一个观点:

  1. 如果它是一个固定维度,CL 将只使用该维度。
  2. 如果是match_parent,CL 将使用父级的确切尺寸
  3. 如果是wrap_content,CL 将询问小部件的大小,然后将其用作固定尺寸
  4. 如果是0dp,CL 将对维度应用约束

1) 固定尺寸

这是一个宽度/高度固定的视图,例如24dp。在这种情况下,CL 将简单地使用该值,不需要对该小部件进行其他关于尺寸的计算。

2)match_parent

我一直认为这对 CL 无效,但事实证明它的行为就像以前的版本一样,它抓取父级的尺寸并将其用作“固定”。与上面的 #1 不同,我认为这可能在计算上更昂贵,因为 CL 现在需要确保已知父维度能够在此处使用它们。我没有这方面的证据,也没有很多经验,因为我一直认为这不是真的有效,所以从未使用过。

3)wrap_content

正如预期的那样,视图必须确定其“所需大小”,因此如果它是 ImageView,它将根据其源向 imageView 询问其尺寸。获得所述数字后,将其用作固定大小,如#1。

4)0dp

这就是 CL 的亮点,通过将约束应用于每个维度(宽度和高度),并让维度的值由算法的结果确定。

那么为什么需要这个(约束宽度/高度)?

首先要了解的是0dp具有展开和环绕行为(和百分比);为了wrap,引擎从视图的维度wrap_content(上面的#3)开始,但在需要时等待约束来更改它。假设您使用wrap作为文本视图的宽度,并且它的约束将其固定到屏幕的边缘(开始/结束到父级)。那些可以拉向不同的方向;文本视图可能希望很小以包裹文本,并且约束将拉动小部件的边缘以到达父开始/结束。这里有一场战斗。(如果文字大于空格,战斗仍然存在,但方向相反)。

存在此属性的原因是因为某些小部件 (_Like textView) 采用了一些快捷方式,并且当存在 时0dp,它们可能并不总是正确更新。重要的是要注意具有 0dp + 权重的 LinearLayouts 做了同样的事情(因此这也是 LL 的问题);通过使用constrainedWidth/Height,像 TextView 这样的小部件可以在需要时正确使用 0dp 和包装行为;它使视图有机会正确地重新测量自身。

当您重用 TexViews 时,这个问题主要表现出来(我不确切知道哪些其他视图会从中受益,但我认为任何有文本的东西都容易有计算快捷方式/黑客,并且可能需要这些额外的信息才能正确触发重新测量)。重用一个像TextView这样的带有文本的小部件,这是最需要的地方,想想一个RecyclerView,你的ViewHolder在一个ConstraintLayout中(很常见),当你滚动时,ViewHolder被重用并重新绑定到另一个“数据模型”如果没有此属性,TextView 将/可能无法为可能出现的文本重新计算其大小。

我希望这是有道理的。

tl;dr:这是解决某些小部件在重用时无法重新计算其尺寸的潜在问题的解决方法,特别是在 RecyclerView 中,但很可能不限于此。

你有它。:)

2018 年 7 月更新:

如果您正在使用ConstraintLayout 1.1.0,则要使用的正确属性将app:layout_constrainedWidth="true"代替旧的app:layout_constraintWidth_default="wrap"(和高度对应物)

2017 年 11 月更新

我正在使用 Constraint Layouts 1.0.2,并且我发现了一个嵌套较少的解决方案app:layout_constraintWidth_default="wrap"(一个在 1.0.0 中引入的属性,但这篇文章使用的 Beta 没有)。

而不是FrameLayout包含 aLinearLayout您现在可以删除所有这些并以这种方式拥有它:

    <android.support.constraint.ConstraintLayout
      android:id="@+id/new_way_container"
      android:layout_height="wrap_content"
      android:layout_width="0dp" // THIS GUY USES ALL THE WIDTH.
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent">

      <TextView
        android:ellipsize="end"
        android:id="@+id/some_text"
        android:layout_height="wrap_content"
        android:layout_width="0dp" //NO WRAP CONTENT, USE CONSTRAINTS
        android:lines="1"
        android:maxLines="1"
        app:layout_constraintEnd_toStartOf="@+id/disclosure_arrow"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintHorizontal_chainStyle="packed" //CHAIN IT for biasing.
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_default="wrap" /> //THIS IS THE KEY THAT WILL CAUSE THIS TO WORK

      <ImageView
        android:id="@+id/disclosure_arrow"
        android:layout_height="wrap_content"
        android:layout_width="10dp"
        app:layout_constraintBottom_toTopOf="@id/some_text"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/some_text"
        app:layout_constraintTop_toBottomOf="@id/some_text"
        app:srcCompat="@drawable/your_vector_image" />
    </android.support.constraint.ConstraintLayout>

这有效地完成了我想要的,没有黑客或指南或硬编码的大小。

TextView 将使用约束提供的大小(在正常情况下,这意味着它要么是错误的,要么会超出“父”),但由于有了新属性,如果内容更小/更大。

我不得不说它比 iOS Priorities 好得多。(至少它对我来说更容易掌握)。为谷歌竖起大拇指:)

旧答案(以防您仍然需要它)。

根据 Nicolas Roard 的回答,我将创建一个自定义容器,它基本上可以计算可用空间,并以编程方式在 TextView 上设置 maxWidth。我没有在项目中添加另一个类、单元测试、可能的错误集等,而是尝试了一种效率稍低的方法来嵌套几个布局;考虑到我们从黎明开始就一直在嵌套布局,并且这不会出现在任何滚动列表视图上或移动太多(或根本不移动),并且我正在使用 ConstraintLayouts 来展平大部分层次结构(前所未有!),那么我认为在得到更好的支持之前不要进行一点嵌套是那么糟糕。

所以我所做的基本上是使用 FrameLayout,它在设计上经过优化(或认为)有一个孩子(但它可以包含更多)。这个 FrameLayout 是一个应用了 ConstraintLayout 规则的框架,如下所示:

  <FrameLayout
      android:id="@+id/hostTextWithCaretContainer"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintRight_toRightOf="parent">

      <!-- MY CONTENT GOES HERE -->

  </FrameLayout>

所以在我的真实应用程序中,这个 FrameLayout 在另一个 ConstraintLayout 里面,它的左边有一个图标和一些其他的东西,但是为了这个例子,想象你必须把这个 FrameLayout 的左/右“固定”到你的任何空间想占领。在这个例子中,你可以看到我parent在所有约束中都使用了,但是这个 FrameLayout 的左右可能还有其他小部件;由于 ConstraintLayout 的魔力,这将占据所有可用空间。

现在到了技巧的第二部分......因为 ConstraintLayout 保证 FrameLayout 将使用我们拥有的“所有空间”并且永远不会更多(或更少),我现在可以在内部使用 LinearLayout......就像这样......</p>

     <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

      <TextView
          android:id="@+id/textView"
          android:layout_height="wrap_content"
          android:layout_width="0dp"
          tools:text="Some Text"
          android:text="Some Text"
          android:textAlignment="viewStart"
          android:layout_gravity="center_vertical"
          android:gravity="start"
          android:ellipsize="end"
          android:maxLines="1"
          android:layout_weight="1"/>

      <ImageView
          android:id="@+id/caret"
          android:layout_width="8dp"
          android:layout_height="8dp"
          app:srcCompat="@drawable/ic_selection"
          android:contentDescription=""
          android:layout_gravity="center_vertical"
          android:layout_marginStart="8dp"
          android:layout_marginEnd="8dp" />

    </LinearLayout>

精明的读者会注意到 LinearLayout 有wrap_content它的宽度,这是非常重要的,因为子 TextView 可以有 0dp 的宽度和weight1 的宽度,这意味着在所有其他小部件都计算出它们的宽度后它将占用所有可用的空闲空间

在这种特殊情况下,另一个孩子(ImageView)caret没有指定权重和固定宽度,因此 TextView 不必与其他任何人共享/分割可用空间,它可以全部占用(但只有可用空间,记住它宽度为 0dp)。

这种效率较低的方法,有效地实现了我想要的,尽管如果你愿意的话,可以使用更少的 ConstraintLayout Magic 。

从好的方面来说,我不必在requestLayout()完成所有数学运算后创建自定义视图、执行数学运算并发出 a;这种效率较低的方法将/应该扩展,并且在 ConstraintLayout 提供有效的替代方案之前,它可能就足够了。

向在社交媒体上回复并最终花时间思考这个问题的 Google 工程师大喊大叫。或许在未来,他们在写关于 ConstraintLayout 1.1 的任务和故事点时,会记住这一点并想出一个好的解决方案

于 2016-11-08T15:50:53.147 回答
4

在我的情况下,来自 Martin 的 2018 年 7 月更新将不起作用:

如果您使用的是 ConstraintLayout 1.1.0,则要使用的正确属性是 app:layout_constrainedWidth="true"代替旧的 app:layout_constraintWidth_default="wrap"(和高度对应物)

我必须android:width="wrap_content"使用app:layout_constrainedWidth="true". android:layout_width="0dp"(match_constraint) 在我的情况下使文本视图拉伸为较短的字符串。

实现相同结果的另一种可能性是使用android:layout_width="0dp"with flagapp:layout_constraintWidth_max="wrap"代替。

更多关于约束布局的标志可以在文档中找到: https ://developer.android.com/reference/android/support/constraint/ConstraintLayout

于 2019-01-03T12:32:46.560 回答
3

您可以在 ImageView 的负边距约束布局中使用相对布局。请注意android:layout_marginRight="26dp"android:layout_marginLeft="-26dp"

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="26dp"
            android:ellipsize="end"
            android:gravity="start"
            android:maxLines="1"
            android:text="Some text" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="-26dp"
            android:layout_toRightOf="@+id/textView"
            android:contentDescription=""
            android:src="@drawable/ic_browser" />
    </RelativeLayout>

</android.support.constraint.ConstraintLayout>

在此处输入图像描述

文本长时: 在此处输入图像描述

于 2017-04-14T15:21:50.007 回答
3

实际上,这是您可以为 TextViews 执行此操作的方法 - 使用android:maxWidth

<TextView
    android:id="@+id/textView7"
    android:maxWidth="200dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:maxLines="1"
    android:ellipsize="end"
    android:text="TextViewTextViewTextViewTextViewTextViewTextViewTextViewTextViewTextView"
    tools:layout_editor_absoluteY="72dp"
    android:layout_marginStart="16dp"
    app:layout_constraintLeft_toLeftOf="parent"
    android:layout_marginLeft="16dp" />

<Button
    android:id="@+id/button20"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    tools:layout_editor_absoluteY="56dp"
    android:layout_marginStart="8dp"
    app:layout_constraintLeft_toRightOf="@+id/textView7"
    android:layout_marginLeft="8dp" />

在此处输入图像描述

这不完全是您所要求的(您需要知道并将大小设置为限制宽度),但这可能会对您有所帮助,直到我们在 ConstraintLayout 本身中支持它。

于 2016-11-04T00:01:41.357 回答
1

这个答案的灵感来自 Martin Marconcini 的答案 ( https://stackoverflow.com/a/40491128/1151701 )。为了减少层次结构,可以将约束布局替换为简单的视图。

      <TextView
        android:ellipsize="end"
        android:id="@+id/some_text"
        android:layout_height="wrap_content"
        android:layout_width="0dp"
        android:lines="1"
        android:maxLines="1"
        app:layout_constraintEnd_toStartOf="@+id/disclosure_arrow"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_default="wrap" /> 

      <ImageView
        android:id="@+id/disclosure_arrow"
        android:layout_height="wrap_content"
        android:layout_width="10dp"
        app:layout_constraintBottom_toTopOf="@id/some_text"
        app:layout_constraintEnd_toEndOf="@id/view_guide"
        app:layout_constraintStart_toEndOf="@id/some_text"
        app:layout_constraintTop_toBottomOf="@id/some_text"
        app:srcCompat="@drawable/your_vector_image" />

      <View
        android:id="@+id/view_guide"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

顺便说一句,我正在使用 ConstraintLayoutandroidx.constraintlayout:constraintlayout:2.0.0-alpha3app:layout_constrainedWidth="true"在这种情况下按预期工作。所以我在app:layout_constraintWidth_default="wrap"这里用过

于 2019-02-22T18:26:50.630 回答