¿Cómo lograr un margen superpuesto / negativo en el diseño de restricción?

117

¿Es posible lograr un margen negativo en el diseño de restricción para lograr la superposición? Estoy tratando de tener una imagen centrada en el diseño y tener una vista de texto tal que se superponga a por x dp. Intenté establecer un valor de margen negativo pero no tuve suerte. Sería genial si hubiera una manera de lograrlo.

enlace
fuente
La guía puede ayudar, no lo he intentado.
Eugen Pechanec

Respuestas:

214

Aclaración: La respuesta a continuación sigue siendo válida, pero quiero aclarar un par de cosas. La solución original colocará una vista con un desplazamiento negativo de facto con respecto a otra vista como se indica y aparecerá en el diseño como se muestra.

Otra solución es usar la propiedad translationY como sugiere Amir Khorsandi aquí . Prefiero esa solución más simple con una advertencia: la traducción se produce después del diseño , por lo que las vistas que están restringidas a la vista desplazada no seguirán la traducción.

Por ejemplo, el siguiente XML muestra dos TextViews inmediatamente debajo de la imagen. Cada vista está restringida de arriba a abajo con la vista que aparece inmediatamente encima.

ingrese la descripción de la imagen aquí

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

Ahora, vamos a traducir el "Di mi nombre" Vista de Texto por 50dpespecificando

android:translationY="-50dp"

Esto produce lo siguiente:

ingrese la descripción de la imagen aquí

El TextView "Diga mi nombre" se ha movido hacia arriba como se esperaba, pero el TextView " Dígalo " no lo ha seguido como podríamos esperar. Esto se debe a que la traducción se produce después del diseño . Aunque la vista se mueve después del diseño, aún se puede hacer clic en ella en la nueva posición.

Entonces, en mi opinión, vaya con translationX y translationY para márgenes negativos en ConstraintLayout si la advertencia anterior no afecta tu diseño; de lo contrario, utilice el widget de espacio como se describe a continuación.


Respuesta original

Aunque no parece que se admitirán márgenes negativos ConstraintLayout, hay una manera de lograr el efecto utilizando las herramientas disponibles y compatibles. Aquí hay una imagen donde el título de la imagen se superpone 22dpdesde la parte inferior de la imagen, efectivamente un -22dpmargen:

ingrese la descripción de la imagen aquí

Esto se logró mediante el uso de un Spacewidget con un margen inferior igual al desplazamiento que desea. El Spacewidget tiene su parte inferior restringida a la parte inferior del ImageView. Ahora todo lo que necesita hacer es restringir la parte superior TextViewcon el título de la imagen a la parte inferior del Spacewidget. El se TextViewcolocará en la parte inferior de laSpace vista ignorando el margen que se estableció.

El siguiente es el XML que logra este efecto. Notaré que lo uso Spaceporque es liviano y está pensado para este tipo de uso, pero podría haber usado otro tipo Viewy hacerlo invisible. (Sin embargo, probablemente necesitará hacer ajustes). También puede definir a Viewcon márgenes cero y la altura del margen de inserción que desee, y restringir la parte superior de TextViewla parte superior de la inserción View.

Otro enfoque más sería superponer la TextViewparte superior ImageViewalineando la parte superior / inferior / izquierda / derecha y hacer los ajustes adecuados en los márgenes / relleno. El beneficio del enfoque que se muestra a continuación es que se puede crear un margen negativo sin mucho cálculo. Eso es todo para decir que hay varias formas de abordar esto.

Actualización: para ver una discusión rápida y una demostración de esta técnica, consulte la publicación del blog de Google Developers Medium .

Margen negativo para 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
fuente
No funciona en mi caso, ImageViewse centra en el padre, TextViewse superpone arriba ImageView. Luego Spacecomete el error cuando la aplicación regresa del fondo. Creo que 0dp, 0dp crea algunos problemas. ¿Qué pasa con el uso Guideline.
illusionJJ
De esta forma no puede funcionar porque la vista tiene limitaciones con el padre.
R4j
¿Por qué necesito un espacio? Lo intenté sin espacio y obtuve el mismo resultado.
Kuzdu
@kuzdu, el punto aquí es que la parte inferior del espacio está restringida en la parte inferior de la vista de imagen, el margen mueve el espacio hacia arriba y luego la parte superior de la vista de texto está restringida en la parte inferior del espacio, lo que crea esta "mitad superpuesta "efecto que estamos tratando de lograr. Publique una respuesta aquí si ha logrado obtenerla sin un espacio para que podamos ver las restricciones que utilizó en sus dos vistas.
Lucas P.
Eres Heisenberg ...
Nahro
62

Otra forma es usar translationXo translationYasí:

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

funcionará como android:layout_marginRight="-25dp"

Amir Khorsandi
fuente
29
Tenga en cuenta esto, dado que la posición real de la vista permanece en la posición original (antes de la traducción), se traducirá solo visualmente. Por lo tanto, los eventos onClick solo se activarán desde la posición anterior.
goemic
La declaración de @goemic parece estar mal, ya que esta no es una animación de interpolación sino una animación de propiedad. (corríjame si me equivoco)
Henry
mala práctica ya que no admite diseños RTL
Salam El-Banna
27

Los márgenes negativos nunca se han admitido oficialmente en RelativeLayout. Los márgenes negativos no se admitirán en ConstraintLayout. [...]

- Romain Guy el 8 de junio de 2016

Siga estos dos temas:

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

Eugen Pechanec
fuente
su idea puede ser que con el diseño de restricciones no necesita un margen negativo. Eso puede ser cierto si tiene todos los elementos en un diseño. A veces, sin embargo, desea agrupar elementos lógicamente como en una tira de comandos hecha de botones, lo que tiene sus propias ventajas de reutilización, claridad y encapsulación. En ese punto, el grupo tendrá una forma rectangular, y es esta limitación en esa forma lo que hace que los márgenes negativos sean útiles y necesarios nuevamente.
AndrewBloom
Los márgenes negativos emulan exactamente el mismo concepto de la línea de base en el texto, siendo el texto una forma compleja dentro de un contenedor rectangular
AndrewBloom
14

Esto es lo que descubrí después de horas de intentar encontrar una solución.

Consideremos dos imágenes, image1 e image2. Image2 debe colocarse en la parte superior de image1 colocada en la parte inferior derecha.

Ejemplo de vistas superpuestas imagen

Podemos usar el widget de Espacio para vistas superpuestas.

Restrinja los cuatro lados del widget Space con los cuatro lados de la imagen1 respectivamente. Para este ejemplo, restrinja el lado izquierdo de image2 con el lado derecho del widget Space y el lado superior de image2 con el lado inferior del widget Space. Esto vinculará image2 con el widget de Espacio y dado que el widget de Espacio está restringido desde todos los lados, podemos definir el sesgo horizontal o vertical requerido que moverá la imagen2 según sea necesario.

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

Además, para colocar image2 en el centro inferior de image1, podemos restringir los lados izquierdo y derecho de image2 con los lados izquierdo y derecho del widget Space respectivamente. De manera similar, podemos colocar image2 en cualquier lugar cambiando las restricciones de image2 con el widget Space.

Swati Navalkar
fuente
¡Gracias por la respuesta!
user3193413
11

Encontré una manera de hacerlo mucho más simple.

Básicamente, tenga ImageView, luego en la Vista de texto agregue la restricción superior para que coincida con la restricción superior de la imagen y simplemente agregue el margen superior de TextView para que coincida para lograr el comportamiento del tipo de margen -ve.

enlace
fuente
9
a menos que la altura de la imagen sea variable
Stas Shusha
Todavía funcionaría. Solo debe tener cuidado con dónde le gustaría que su widget superior superponga la imagen, establecer el margen desde la derecha o desde la parte inferior podría ser una mejor opción, o podría usar el sesgo, todo lo cual responde a la pregunta .
Gabriel Vasconcelos
4

Esto ayudará a muchos

En mi caso quiero mi diseño así:

después

Significa que quiero que mi imagen muestre la mitad de su ancho, por lo que básicamente necesito un margen negativo de la mitad del ancho real de la imagen, pero todo mi diseño en el diseño de restricción y el diseño de restricción no permite un margen negativo, así que lo logré con el siguiente código

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

De modo que ImageView terminará al comienzo de la guía. y el efecto es el mismo que un margen negativo al comienzo de 50dp.

Y también si el ancho de su vista no es fijo y está en porcentaje para que pueda colocar la guía con porcentaje y lograr el efecto que desee

Codificación feliz :)

Vijay Makwana
fuente
0

Solo necesita usar el widget Space en su diseño

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

Hagar Magdy
fuente
0

Esta es una pregunta antigua pero muy formulada, la forma más rápida de lograr esto es restringiendo la parte superior e inferior al lado de la vista al que desea anclar, así:

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

Esto lo centrará en la línea inferior de la vista, centrado horizontalmente.

Ahmed Awad
fuente
-1

Una forma sencilla.

No estoy seguro de la mejor manera.

Simplemente envuelva usando 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>
최봉재
fuente
Esto agrega un poco más de anidación, PERO funciona para animar vistas desde la parte superior, a diferencia de las otras soluciones aquí, que asumen que la Vista se mostrará por completo. ¡Gracias!
Leo apoya a Monica Cellio
2
Esta respuesta necesita mucha más información. No está claro cómo se puede usar poner algo en un LinearLayout para lograr la superposición.
Robert Liberatore