ConstraintLayout Цепи и эллипс текста + изображение справа

ОБНОВЛЕНИЕ ИЮЛЬ 2018:

Если вы используете ConstraintLayout 1.1.0, правильное свойство - app:layout_constrainedWidth="true" вместо старого app:layout_constraintWidth_default="wrap" (и аналог высоты). Смотрите обновленный ответ.

ОБНОВЛЕНИЕ НОЯБРЯ 2017:

Если вы используете ConstraintLayout 1.0.0 стабильный (или выше) (в настоящее время 1.0.2), см. Обновленный Ответ для более простого решения без необходимости вложения макетов.

Оригинальный вопрос:

Использование ConstraintLayouts-Beta3 выпущенного 3 ноября 2016 г. (источник)

Я пытаюсь сделать следующее:

|                                        |
|<-[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"/>

Это выглядит нормально, но когда текст длиннее доступного пространства...

|                                        |
|<-[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: я пробовал это и с бета2, на самом деле, похоже, что в бета3 есть ошибка в визуальном редакторе.

ОБНОВЛЕНИЕ: я попытался повторить это в Xcode/AutoLayout:

Вот как это выглядит с коротким текстом

Short Text

Теперь та же схема, я просто набираю длинный текст в текстовом представлении…

Long Text

Как видите, ограничение следа (справа) для вида изображения говорит: вы на 8 или более точках от правого поля.

Он также прикреплен слева к метке (textView).

Из того, что я только что узнал из Twitter, это может быть невозможно в Android ConstraintLayout в данный момент: Source

Ответ 1

ОБНОВЛЕНИЕ ИЮЛЬ 2018:

Если вы используете ConstraintLayout 1.1.0, правильное свойство - app:layout_constrainedWidth="true" вместо старого app:layout_constraintWidth_default="wrap" (и аналог высоты)

ОБНОВЛЕНИЕ НОЯБРЯ 2017

Я использую Constraint Layouts 1.0.2, и я нашел менее вложенное решение, используя app:layout_constraintWidth_default="wrap" (свойство, которое было введено в 1.0.0, но бета-версия, которую использовал этот пост, не имело).

Вместо FrameLayout который содержит LinearLayout, теперь вы можете удалить все это и сделать это следующим образом:

    <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. (По крайней мере, мне намного легче понять это). Недурно для Google на этом :)

СТАРЫЙ ОТВЕТ (если он вам все еще нужен).

Основываясь на ответе Николаса Роарда, я собирался создать собственный контейнер, который в основном вычислял бы доступное пространство, и программно устанавливал maxWidth в TextView. Вместо того, чтобы добавить в проект другой класс, unit тест, возможный набор ошибок и т.д., Я попробовал несколько менее эффективный способ вложения нескольких макетов; учитывая, что мы вкладывали макеты с эпохи рассвета и что это не будет отображаться ни в одном прокручиваемом списке, ни слишком много (или вообще не двигались), а также что я использую 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 внутри… вот так…

     <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 и weight 1, что означает, что он займет все доступное свободное пространство после того, как все другие виджеты вычислили свою ширину,

В данном конкретном случае, а другой ребенок (ImageView) caret не имеет указанный веса и фиксированную ширину, поэтому TextView не придется делить/разделить свободное пространство с кем - либо, и он может принять все это (но только свободное пространство, помните, его ширина составляет 0dp).

Этот менее эффективный подход позволяет добиться именно того, чего я хотел, хотя и с меньшим количеством ConstraintLayout Magic, если хотите.

С другой стороны, мне не нужно было создавать пользовательское представление, выполнять математику и выполнять requestLayout() после того, как все мои вычисления были выполнены; этот менее эффективный подход будет/должен масштабироваться, и пока ConstraintLayout не предложит правильную альтернативу, этого может быть достаточно, как есть.

Привет инженерам Google, которые ответили в социальных сетях и в итоге нашли время подумать об этом.Возможно, в будущем, когда они будут писать задачи и сюжеты о ConstraintLayout 1.1, они помнят об этом и придумают хорошее решение.

Ответ 2

Собственно, вот как вы могли это сделать для 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.

Ответ 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>

введите описание изображения здесь

Когда текст длинный: введите описание изображения здесь

Ответ 4

В моем случае обновление от июля 2018 года от Мартина не будет работать:

Если вы используете 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" с флагом app:layout_constraintWidth_max="wrap".

Подробнее о флагах для макета ограничения можно найти в документе: https://developer.android.com/reference/android/support/constraint/ConstraintLayout

Ответ 5

Этот ответ основан на ответе Мартина Маркончини (fooobar.com/info/842707/...). Чтобы уменьшить иерархию, вы можете заменить макет ограничения простым представлением.

      <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" />

Кстати, я использую ConstraintLayout androidx.constraintlayout:constraintlayout:2.0.0-alpha3 но app:layout_constrainedWidth="true" в этом случае работает app:layout_constrainedWidth="true". Поэтому я использовал app:layout_constraintWidth_default="wrap" здесь