Как добиться перекрытия / отрицательного поля в макете ограничений?

118

Можно ли добиться отрицательного запаса на макете ограничения для достижения перекрытия? Я пытаюсь центрировать изображение на макете и иметь текстовое представление, чтобы оно перекрывалось x dp. Я попытался установить отрицательное значение маржи, но безуспешно. Было бы здорово, если бы был способ добиться этого.

связь
источник
Руководство может помочь, я не пробовал.
Eugen Pechanec

Ответы:

214

Уточнение: ответ ниже остается в силе, но я хочу прояснить пару вещей. Исходное решение поместит вид с фактическим отрицательным смещением по отношению к другому виду, как указано, и появится в макете, как показано.

Другое решение - использовать свойство translationY, предложенное здесь Амиром Хорсанди . Я предпочитаю это решение как более простое с одной оговоркой: перевод выполняется после макета , поэтому представления, ограниченные смещенным представлением, не будут следовать за переводом.

Например, следующий XML- код отображает два TextView непосредственно под изображением. Каждое представление ограничено сверху вниз с видом, который появляется непосредственно над ним.

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

<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:tint="#388E3C"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/ic_action_droid" />

    <TextView
        android:id="@+id/sayName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Say my name."
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        app:layout_constraintEnd_toEndOf="@+id/imageView"
        app:layout_constraintStart_toStartOf="@+id/imageView" />

    <TextView
        android:id="@+id/sayIt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Say it."
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintEnd_toEndOf="@+id/sayName"
        app:layout_constraintStart_toStartOf="@+id/sayName"
        app:layout_constraintTop_toBottomOf="@id/sayName" />
</androidx.constraintlayout.widget.ConstraintLayout>

Теперь давайте переведем TextView « Назови мое имя» вверх 50dp, указав

android:translationY="-50dp"

Это дает следующее:

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

TextView «Say my name» сдвинулся вверх, как и ожидалось, но TextView «Say it» не последовал за ним, как мы могли ожидать. Это потому, что перевод происходит после верстки . Несмотря на то, что вид перемещается после макета, его все еще можно сделать кликабельным в новом месте.

Итак, IMO, используйте translationX и translationY для отрицательных полей в ConstraintLayout, если указанное выше предостережение не влияет на ваш макет; в противном случае используйте виджет пространства, как показано ниже.


Оригинальный ответ

Хотя не похоже, что отрицательные поля будут поддерживаться ConstraintLayout, есть способ добиться этого эффекта с помощью доступных и поддерживаемых инструментов. Вот изображение, на котором название изображения перекрывается 22dpснизу изображения - фактически -22dpполе:

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

Это было достигнуто с помощью Spaceвиджета с нижним полем, равным желаемому смещению. Затем Spaceнижняя часть виджета ограничивается нижней частью ImageView. Теперь все, что вам нужно сделать, это связать верхнюю часть TextViewс заголовком изображения с нижней частью Spaceвиджета. Значок TextViewбудет расположен в нижней части Spaceпредставления, игнорируя установленное поле.

Ниже приводится XML, который реализует этот эффект. Замечу, что я использую, Spaceпотому что он легкий и предназначен для этого типа использования, но я мог бы использовать другой тип Viewи сделать его невидимым. (Тем не менее, вам, вероятно, придется внести изменения.) Вы также можете определить a Viewс нулевыми полями и высотой поля вставки, которое вы хотите, и ограничить TextViewверхнюю часть вставки до верхней части вставки View.

Еще один подход состоит в том, чтобы наложить TextViewверхнюю часть изображения на верхнюю ImageView/ нижнюю / левую / правую границы и внести соответствующие изменения в поля / отступы. Преимущество продемонстрированного ниже подхода заключается в том, что отрицательная маржа может быть создана без больших вычислений. Это все, что говорит о том, что есть несколько подходов к этому.

Обновление: быстрое обсуждение и демонстрация этого метода см. В сообщении блога Google Developers Medium .

Отрицательная маржа для ConstraintLayoutXML

<android.support.constraint.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@mipmap/ic_launcher" />

    <android.support.v4.widget.Space
        android:id="@+id/marginSpacer"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="22dp"
        app:layout_constraintBottom_toBottomOf="@+id/imageView"
        app:layout_constraintLeft_toLeftOf="@id/imageView"
        app:layout_constraintRight_toRightOf="@id/imageView" />

    <TextView
        android:id="@+id/editText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Say my name"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/marginSpacer" />
</android.support.constraint.ConstraintLayout>
Cheticamp
источник
Не работает в моем случае, ImageViewсосредоточено на родителе, TextViewперекрывается выше ImageView. Затем Spaceделает ошибку, когда приложение возвращается из фона. Я думаю, что 0dp, 0dp создают проблемы. А как насчет просто использования Guideline.
illusionJJ
Этот способ не может работать, поскольку у представления есть ограничения с родителем.
R4j
Зачем мне нужно пространство? Я пробовал без пробела, и результат тот же.
kuzdu
@kuzdu, дело в том, что нижняя часть пространства ограничена в нижней части imageView, поле перемещает пространство вверх, а затем верх текстового представления ограничивается нижней частью пространства, что создает это "половинное наложение" «эффекта, которого мы пытаемся добиться. Отправьте здесь ответ, если вам удалось получить его без пробела, чтобы мы могли увидеть ограничения, которые вы использовали для двух представлений.
Лукас П.
62

Другой способ - использовать translationXили translationYвот так:

  <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" 
                android:translationX="25dp"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"/>

это будет работать как android:layout_marginRight="-25dp"

Амир Хорсанди
источник
29
Помните об этом, так как фактическое положение вида остается в исходном положении (до перевода), оно будет переведено только визуально. Таким образом, события onClick будут запускаться только из старой позиции.
goemic
Заявление @goemic кажется неправильным, поскольку это не анимация, а анимация свойств. (пожалуйста, поправьте меня, если я ошибаюсь)
Генри
плохая практика, поскольку он не поддерживает макеты RTL
Салам Эль-Банна
27

Отрицательные поля никогда официально не поддерживались в RelativeLayout. Отрицательные поля не поддерживаются в ConstraintLayout. [...]

- Ромен Гай, 8 июня 2016 г.

Следуйте этим двум вопросам:

https://code.google.com/p/android/issues/detail?id=212499 https://code.google.com/p/android/issues/detail?id=234866

Евгений Печанец
источник
их идея может заключаться в том, что с constraintlayout вам не нужен отрицательный запас. Это может быть правдой, если у вас есть все элементы на одном макете. Иногда вы хотите логически сгруппировать элементы, например, в полосе команд, состоящей из кнопок, которая имеет свои преимущества в отношении повторного использования, ясности и инкапсуляции. В этот момент группа будет иметь прямоугольную форму, и именно это ограничение этой формы делает отрицательные поля полезными и необходимыми снова.
AndrewBloom
Отрицательные поля имитируют точно такую ​​же концепцию базовой линии в тексте, будучи текстом сложной формы в прямоугольном контейнере
AndrewBloom
14

Это то, что я понял после нескольких часов попыток найти решение.

Давайте рассмотрим два изображения: image1 и image2. Изображение2 должно быть помещено поверх изображения1, расположенного в правом нижнем углу.

Пример перекрывающихся видов образ

Мы можем использовать виджет Space для перекрытия просмотров.

Ограничьте четыре стороны виджета Space четырьмя сторонами изображения1 соответственно. В этом примере ограничьте левую сторону image2 правой стороной виджета Space, а верхнюю сторону image2 - нижней стороной виджета Space. Это свяжет image2 с виджетом Space, и, поскольку виджет Space ограничен со всех сторон, мы можем определить необходимое горизонтальное или вертикальное смещение, которое будет перемещать image2 по мере необходимости.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Player">
 <ImageView
     android:id="@+id/image1"
     android:layout_width="250dp"
     android:layout_height="167dp"
     android:src="@android:color/holo_green_dark"
     app:layout_constraintEnd_toEndOf="parent"
     app:layout_constraintStart_toStartOf="parent"
     app:layout_constraintTop_toTopOf="parent" />
 <Space
     android:id="@+id/space"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     app:layout_constraintBottom_toBottomOf="@+id/image1"
     app:layout_constraintEnd_toEndOf="@+id/image1"
     app:layout_constraintHorizontal_bias="0.82"
     app:layout_constraintStart_toStartOf="@+id/image1"
     app:layout_constraintTop_toTopOf="@+id/image1"
     app:layout_constraintVertical_bias="0.62" />
 <ImageView
     android:id="@+id/image2"
     android:layout_width="82dp"
     android:layout_height="108dp"
     android:src="@android:color/holo_green_light"
     app:layout_constraintStart_toEndOf="@+id/space"
     app:layout_constraintTop_toBottomOf="@+id/space" />
 </android.support.constraint.ConstraintLayout>

Кроме того, чтобы расположить image2 в центре-низу изображения1, мы можем ограничить левую и правую стороны изображения2 с помощью левой и правой сторон виджета Space соответственно. Точно так же мы можем разместить image2 где угодно, изменив ограничения image2 с помощью виджета Space.

Свати Навалкар
источник
Спасибо за ответ!
user3193413
11

Я нашел способ сделать это намного проще.

В основном есть ImageView, затем в текстовом представлении добавьте верхнее ограничение, чтобы оно соответствовало верхнему ограничению изображения, и просто добавьте верхний край TextView для соответствия, чтобы добиться поведения типа поля -ve.

связь
источник
9
если высота изображения не переменная
Стас Шуша
Это все равно будет работать. Вам просто нужно быть осторожным с тем, где вы хотите, чтобы ваш верхний виджет наложил изображение, установка поля справа или снизу может быть лучшим вариантом, или вы можете использовать смещение, и все это отвечает на вопрос .
Габриэль Васконселос
4

Это поможет многим

В моем случае мне нужен такой дизайн:

после

Означает, что я хочу, чтобы мое изображение отображало половину их ширины, поэтому в основном мне нужен отрицательный запас в половину фактической ширины изображения, но весь мой макет в макете ограничений и макет ограничения не допускает отрицательного поля, поэтому я достиг этого с помощью кода ниже

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:scaleType="centerCrop"
        android:src="@drawable/ic_launcher_background"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/guideline"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="50dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

Так что ImageView будет заканчиваться в начале руководства. и эффект такой же, как и при отрицательном поле в начале 50dp.

А также, если ширина вашего представления не фиксирована, а в процентах, чтобы вы могли разместить направляющую с процентами и достичь любого эффекта, который вы хотите

Удачного кодирования :)

Виджай Маквана
источник
0

Вам нужно только использовать виджет Space в вашем макете

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

<Space
    android:id="@+id/negative_margin"
    android:layout_width="16dp"
    android:layout_height="16dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintRight_toLeftOf="parent"/>

<Button
    android:id="@+id/button"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Widget who needs negative margin"
    app:layout_constraintTop_toBottomOf="@+id/negative_margin"
    app:layout_constraintLeft_toLeftOf="@+id/negative_margin" />

Агарь Магди
источник
0

Это старый вопрос, но его очень часто задают, самый быстрый способ добиться этого - ограничить верх и низ стороны представления, к которому вы хотите привязаться, например:

        <androidx.appcompat.widget.AppCompatImageView
        android:layout_width="55dp"
        android:layout_height="55dp"
        app:layout_constraintBottom_toBottomOf="@+id/parent_view_id"
        app:layout_constraintTop_toBottomOf="@+id/parent_view_id"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent" />

Это будет центрировать его в нижней строке обзора по горизонтали.

Ахмед Авад
источник
-1

Простой способ.

Я не уверен, что лучше всего.

Просто оберните с помощью LinearLayout

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
 <View
 android:layout_width="wrap_content"
 android:layout_marginLeft="-20dp"
 android:layout_height="wrap_content"/>
</LinearLayout>
최봉재
источник
Это добавляет немного больше вложенности, НО это работает для анимации представлений сверху, в отличие от других решений здесь, которые предполагают, что представление будет отображаться полностью. Спасибо!
Лео поддерживает Монику Челлио
2
Для этого ответа требуется гораздо больше информации. Непонятно, как размещение чего-либо в LinearLayout может использоваться для достижения перекрытия.
Роберт Либераторе