it-swarm.com.ru

ConstraintLayout Chains и многоточие текста + изображение справа

ОБНОВЛЕНИЕ ИЮЛЬ 2018 ГОДА:

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

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

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

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

Использование ConstraintLayouts-Beta3, выпущенного 3 ноября 2016 года. ( Source

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

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

UPDATE: я попытался повторить это в Xcode/AutoLayout:

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

Short Text

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

Long Text

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

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

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

23
Martin Marconcini

ОБНОВЛЕНИЕ ИЮЛЬ 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. Вместо того, чтобы добавить в проект другой класс, модульный тест, возможный набор ошибок и т.д., Я попытался использовать несколько менее эффективный метод ffective для размещения нескольких макетов; учитывая, что мы вкладывали макеты с эпохи рассвета и что это не будет отображаться ни в одном прокручиваемом списке, ни слишком много (или вообще не двигались), и что я использую ConstraintLayouts, чтобы сгладить большую часть иерархии (как никогда раньше) !), тогда я не думаю, что небольшая вложенность , пока это не будет лучше поддержано, это плохо. 

Итак, в основном я использовал FrameLayout, который by design оптимизирован (или думал), чтобы иметь одного ребенка (хотя он может содержать больше). Это 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>

Так что в моем приложении real этот 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, они помнят об этом и придумают хорошее решение. 

32
Martin Marconcini

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

 enter image description here

Это не совсем то, что вы просили (вам нужно знать и установить размер для ограничения ширины), но это может помочь вам, пока мы не будем поддерживать это в самом ConstraintLayout.

3
Nicolas Roard

Вы можете использовать относительный макет в макете ограничений с отрицательным полем для 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>

 enter image description here

Когда текст длинный:  enter image description here

2
Dmitry

Этот ответ основан на ответе Мартина Маркончини ( 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" />

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

0
Ali Mehrpour

В моем случае обновление от июля 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" вместо этого.

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

0
Mikołaj Demków