Comment obtenir un chevauchement / une marge négative sur la disposition des contraintes?

118

Est-il possible d'obtenir une marge négative sur la disposition des contraintes pour obtenir un chevauchement? J'essaye d'avoir une image centrée sur la mise en page et d'avoir une vue de texte telle qu'elle chevauche un par x dp. J'ai essayé de définir une valeur de marge négative mais pas de chance. Ce serait formidable s'il y avait un moyen d'y parvenir.

liaison
la source
La ligne directrice peut aider, je n'ai pas essayé.
Eugen Pechanec

Réponses:

214

Clarification: La réponse ci-dessous reste valable, mais je tiens à clarifier quelques points. La solution d'origine placera une vue avec un décalage négatif de facto par rapport à une autre vue comme indiqué et apparaîtra dans la mise en page comme indiqué.

Une autre solution consiste à utiliser la propriété translationY comme suggéré par Amir Khorsandi ici . Je préfère que cette solution soit plus simple avec une mise en garde: la traduction se produit après la mise en page , donc les vues qui sont contraintes à la vue déplacée ne suivront pas la traduction.

Par exemple, le code XML suivant affiche deux TextViews immédiatement sous l'image. Chaque vue est contrainte de haut en bas avec la vue qui apparaît immédiatement au-dessus.

entrez la description de l'image ici

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

Maintenant, nous allons traduire le « Say my name » TextView par 50dpen spécifiant

android:translationY="-50dp"

Cela produit ce qui suit:

entrez la description de l'image ici

Le TextView "Dites mon nom" s'est déplacé vers le haut comme prévu, mais le TextView "Dites-le" ne l' a pas suivi comme on pouvait s'y attendre. En effet, la traduction a lieu après la mise en page . Bien que la vue se déplace après la mise en page, elle peut toujours être rendue cliquable dans la nouvelle position.

Donc, OMI, optez pour translationX et translationY pour les marges négatives dans ConstraintLayout si la mise en garde ci-dessus n'affecte pas votre mise en page; sinon, utilisez le widget d' espace comme indiqué ci-dessous.


Réponse originale

Bien qu'il ne semble pas que les marges négatives soient prises en charge dans ConstraintLayout, il existe un moyen d'obtenir l'effet en utilisant les outils disponibles et pris en charge. Voici une image où le titre de l'image se chevauche 22dpdepuis le bas de l'image - en fait une -22dpmarge:

entrez la description de l'image ici

Cela a été accompli en utilisant un Spacewidget avec une marge inférieure égale au décalage souhaité. Le Spacewidget a alors son bas contraint au bas du fichier ImageView. Il ne vous reste plus qu'à contraindre le haut du TextViewavec le titre de l'image au bas du Spacewidget. Le TextViewsera positionné en bas de la Spacevue en ignorant la marge qui a été définie.

Voici le XML qui accomplit cet effet. Je noterai que je l'utilise Spacecar il est léger et destiné à ce type d'utilisation, mais j'aurais pu utiliser un autre type de Viewet le rendre invisible. (Vous devrez probablement faire des ajustements, cependant.) Vous pouvez également définir un Viewavec des marges nulles et la hauteur de la marge de l'encart que vous voulez, et contraindre TextViewle haut de l'encart au haut de l'encart View.

Une autre approche encore consisterait à superposer TextViewle dessus ImageViewen alignant dessus / bas / gauche / droite et faire des ajustements appropriés aux marges / rembourrage. L'avantage de l'approche démontrée ci-dessous est qu'une marge négative peut être créée sans beaucoup de calcul. Tout cela pour dire qu'il existe plusieurs manières d'aborder cela.

Mise à jour: pour une discussion rapide et une démonstration de cette technique, consultez l' article de blog Google Developers Medium .

Marge négative pour 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>
Chéticamp
la source
Ne fonctionne pas dans mon cas, ImageViewest centré sur le parent, TextViewse chevauche ci-dessus ImageView. Puis Spacefait l'erreur lorsque l'application revient de l'arrière-plan. Je pense que 0dp, 0dp pose quelques problèmes. Qu'en est-il juste utiliser Guideline.
illusionJJ
Cette façon ne peut pas fonctionner pour la vue a des contraintes avec le parent.
R4j
Pourquoi ai-je besoin d'un espace? J'ai essayé sans espace et j'ai le même résultat.
kuzdu
@kuzdu le point ici est que le bas de l'espace est contraint en bas de l'imageView, la marge déplace l'espace vers le haut, puis le haut de la vue texte est contraint en bas de l'espace ce qui crée cette "demi-superposition "effet que nous essayons d'obtenir. Veuillez poster une réponse ici si vous avez réussi à l'obtenir sans espace afin que nous puissions voir les contraintes que vous avez utilisées sur vos deux vues.
Lucas P.
You're Heisenberg ...
Nahro
62

Une autre façon est d'utiliser translationXou translationYcomme ceci:

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

ça fonctionnera comme android:layout_marginRight="-25dp"

Amir Khorsandi
la source
29
Attention, puisque la position réelle de la vue reste à la position d'origine (avant traduction), elle ne sera traduite que visuellement. Ainsi, les événements onClick ne seront déclenchés qu'à partir de l'ancienne position.
goemic
La déclaration de @ goemic semble être fausse, car il ne s'agit pas d'une animation d'interpolation mais d'une animation de propriété. (corrigez-moi si je me trompe)
Henry
mauvaise pratique car il ne prend pas en charge les mises en page RTL
Salam El-Banna
27

Les marges négatives n'ont jamais été officiellement prises en charge dans RelativeLayout. Les marges négatives ne seront pas prises en charge dans ConstraintLayout. [...]

- Romain Guy le 8 juin 2016

Suivez ces deux questions:

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

Eugen Pechanec
la source
leur idée peut être qu'avec la mise en page des contraintes, vous n'avez pas besoin de marge négative. Cela peut être vrai si vous avez tous les éléments sur une seule mise en page. Parfois, vous souhaitez regrouper logiquement des éléments comme dans une bande de commandes composée de boutons, ce qui a ses propres avantages en termes de réutilisabilité, de clarté et d'encapsulation. À ce stade, le groupe aura une forme rectangulaire, et c'est cette limitation de cette forme qui rend les marges négatives utiles et nécessaires à nouveau.
AndrewBloom
Les marges négatives émulent exactement le même concept de la ligne de base sur le texte, étant du texte une forme complexe dans un conteneur rectangulaire
AndrewBloom
14

C'est ce que j'ai compris après des heures à essayer de trouver une solution.

Considérons deux images, image1 et image2. Image2 doit être placée au-dessus de image1 positionnée sur le côté inférieur droit.

Exemple de vues superposées image

Nous pouvons utiliser le widget Espace pour les vues qui se chevauchent.

Contraignez les quatre côtés du widget Espace avec les quatre côtés de l'image1 respectivement. Pour cet exemple, contraignez le côté gauche de l'image2 avec le côté droit du widget Espace et le côté supérieur de l'image2 avec le côté inférieur du widget Espace. Cela liera image2 avec le widget Espace et comme le widget Espace est contraint de tous les côtés, nous pouvons définir le biais horizontal ou vertical requis qui déplacera l'image2 selon les besoins.

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

De plus, pour positionner image2 au centre-bas de image1, nous pouvons contraindre les côtés gauche et droit de l'image2 avec les côtés gauche et droit du widget Espace respectivement. De même, nous pouvons placer image2 n'importe où en modifiant les contraintes de l'image2 avec le widget Espace.

Swati Navalkar
la source
Merci pour la réponse!
user3193413
11

J'ai trouvé un moyen beaucoup plus simple de le faire.

Fondamentalement, ayez ImageView, puis sur la vue texte, ajoutez la contrainte supérieure pour correspondre à la contrainte supérieure de l'image et ajoutez simplement la marge supérieure de TextView pour qu'elle corresponde pour obtenir le comportement de type de marge -ve.

liaison
la source
9
sauf si la hauteur de l'image est variable
Stas Shusha
Cela fonctionnerait toujours. Vous devez juste faire attention à l'endroit où vous souhaitez que votre widget supérieur superpose l'image, définir la marge à partir de la droite ou du bas pourrait être une meilleure option, ou vous pouvez utiliser le biais, qui répondent tous à la question .
Gabriel Vasconcelos
4

Cela aidera beaucoup

Dans mon cas, je veux ma conception comme ceci:

après

Cela signifie que je veux que mon image affiche la moitié de sa largeur, donc j'ai essentiellement besoin d'une marge négative de la moitié de la largeur réelle de l'image, mais toute ma mise en page dans la mise en page de contrainte et la mise en page de contrainte ne permet pas de marge négative, donc j'ai réalisé cela avec le code ci-dessous

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

Pour qu'ImageView se termine au début de la ligne de guidage. et l'effet est le même que la marge négative au début de 50dp.

Et aussi si la largeur de votre vue n'est pas fixe et qu'elle est en pourcentage afin que vous puissiez placer une ligne de guidage avec un pourcentage et obtenir l'effet que vous voulez

Bon codage :)

Vijay Makwana
la source
0

Il vous suffit d'utiliser le widget Espace dans votre mise en page

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

Agar Magdy
la source
0

C'est une vieille question pourtant très posée, le moyen le plus rapide d'y parvenir est de contraindre le haut et le bas du côté de la vue sur laquelle vous souhaitez ancrer, comme ceci:

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

Cela le centrera sur la ligne inférieure de la vue, centrée horizontalement.

Ahmed Awad
la source
-1

Une manière simple.

Je ne suis pas sûr de la meilleure façon.

Enveloppez simplement en utilisant 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>
최봉재
la source
Cela ajoute un peu plus d'imbrication, MAIS cela fonctionne pour animer les vues depuis le haut, contrairement aux autres solutions ici, qui supposent que la vue sera complètement affichée. Merci!
Leo soutient Monica Cellio le
2
Cette réponse nécessite beaucoup plus d'informations. On ne sait pas comment mettre quelque chose dans un LinearLayout peut être utilisé pour accomplir un chevauchement.
Robert Liberatore