Como obter sobreposição / margem negativa no Layout de restrição?

118

É possível obter margem negativa no layout de restrição para obter sobreposição? Estou tentando ter uma imagem centralizada no layout e ter uma visualização de texto de forma que se sobreponha a x dp. Tentei definir o valor da margem negativa, mas sem sorte. Seria ótimo se houvesse uma maneira de conseguir isso.

vinculo
fonte
A diretriz pode ajudar, ainda não tentei.
Eugen Pechanec

Respostas:

214

Esclarecimento: a resposta abaixo continua válida, mas quero esclarecer algumas coisas. A solução original colocará uma vista com um deslocamento negativo de fato em relação a outra vista conforme declarado e aparecerá no layout conforme mostrado.

Outra solução é usar a propriedade translationY conforme sugerido por Amir Khorsandi aqui . Eu prefiro essa solução mais simples com uma ressalva: a tradução ocorre após o layout , de modo que as vistas que são restritas à vista deslocada não sigam a tradução.

Por exemplo, o seguinte XML exibe dois TextViews imediatamente abaixo da imagem. Cada visualização é restrita de cima para baixo com a visualização que aparece imediatamente acima dela.

insira a descrição da imagem aqui

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

Agora, vamos traduzir o "Diga meu nome" TextView por 50dpespecificando

android:translationY="-50dp"

Isso produz o seguinte:

insira a descrição da imagem aqui

O TextView "Diga meu nome" mudou conforme o esperado, mas o TextView "Diga" não o acompanhou como esperávamos. Isso ocorre porque a tradução ocorre após o layout . Embora a visualização se mova após o layout, ela ainda pode ser clicada na nova posição.

Então, IMO, vá com translationX e translationY para margens negativas em ConstraintLayout se a advertência acima não afetar seu layout; caso contrário, vá com o widget de espaço conforme descrito abaixo.


Resposta original

Embora não pareça que as margens negativas serão suportadas ConstraintLayout, existe uma maneira de conseguir o efeito usando as ferramentas disponíveis e suportadas. Aqui está uma imagem onde o título da imagem é sobreposto 22dpna parte inferior da imagem - efetivamente uma -22dpmargem:

insira a descrição da imagem aqui

Isso foi feito usando um Spacewidget com uma margem inferior igual ao deslocamento que você deseja. O Spacewidget, então, tem sua parte inferior restrita à parte inferior do ImageView. Agora tudo que você precisa fazer é restringir a parte superior do TextViewcom o título da imagem na parte inferior do Spacewidget. O TextViewserá posicionado na parte inferior da Spacevisualização, ignorando a margem que foi configurada.

A seguir está o XML que realiza esse efeito. Vou notar que uso Spaceporque é leve e destinado a esse tipo de uso, mas poderia ter usado outro tipo de Viewe torná-lo invisível. (Você provavelmente precisará fazer ajustes, no entanto.) Você também pode definir a Viewcom margens zero e a altura da margem de inserção desejada e restringir a parte superior da TextViewao topo da inserção View.

Outra abordagem seria sobrepor a TextViewparte ImageViewsuperior alinhando parte superior / inferior / esquerda / direita e fazer ajustes adequados nas margens / preenchimento. O benefício da abordagem demonstrada a seguir é que uma margem negativa pode ser criada sem muitos cálculos. Isso quer dizer que existem várias maneiras de abordar isso.

Atualização: para uma rápida discussão e demonstração dessa técnica, consulte a postagem do blog Google Developers Medium .

Margem negativa 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
fonte
Não funciona no meu caso, ImageViewé centrado no pai, TextViewé sobreposição acima ImageView. Em seguida, Spacecomete o erro quando o App volta do fundo. Acho que 0dp, 0dp causa alguns problemas. Que tal apenas usar Guideline.
illusionJJ
Desta forma, não pode funcionar para o modo de exibição com restrições com o pai.
R4j
Por que preciso de um Espaço? Tentei sem espaço e obtive o mesmo resultado.
kuzdu
@kuzdu, o ponto aqui é que a parte inferior do espaço é restringida na parte inferior da imageView, a margem move o espaço para cima e, em seguida, a parte superior da visualização do texto é restringida na parte inferior do espaço, o que cria essa "meia sobreposição "efeito que estamos tentando alcançar. Por favor, poste uma resposta aqui se você conseguiu sem um espaço para que possamos ver as restrições que você usou em suas duas visualizações.
Lucas P.
Você é Heisenberg ...
Nahro
62

Outra forma é usar translationXou translationYassim:

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

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

Amir Khorsandi
fonte
29
Esteja ciente disso, uma vez que a posição real da vista permanece na posição original (antes da tradução), ela será traduzida apenas visualmente. Assim, os eventos onClick serão acionados apenas na posição anterior.
goemic
A declaração de @goemic parece estar errada, uma vez que esta não é uma animação de interpolação, mas uma animação de propriedade. (corrija-me se eu estiver errado)
Henry
má prática, pois não suporta layouts RTL
Salam El-Banna
27

Margens negativas nunca foram oficialmente suportadas no RelativeLayout. Margens negativas não serão suportadas no ConstraintLayout. [...]

- Romain Guy em 8 de junho de 2016

Siga estas duas questões:

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

Eugen Pechanec
fonte
a ideia deles pode ser que, com o layout de restrição, você não precisa de margem negativa. Isso pode ser verdade se você tiver todos os elementos em um layout. Às vezes, entretanto, você deseja agrupar elementos logicamente, como em uma faixa de comando feita de botões, o que tem suas próprias vantagens de reutilização, clareza e encapsulamento. Nesse ponto, o grupo terá uma forma retangular, e é essa limitação dessa forma que torna as margens negativas úteis e necessárias novamente.
AndrewBloom
As margens negativas emulam exatamente o mesmo conceito da linha de base do texto, sendo o texto uma forma complexa dentro de um contêiner retangular
AndrewBloom
14

Isso é o que descobri depois de horas tentando encontrar uma solução.

Vamos considerar duas imagens, imagem1 e imagem2. Image2 deve ser colocado no topo da image1 posicionada no lado inferior direito.

Exemplo de vistas sobrepostas imagem

Podemos usar o widget Espaço para visualizações sobrepostas.

Restrinja os quatro lados do widget de Espaço com os quatro lados da imagem1 respectivamente. Para este exemplo, restrinja o lado esquerdo da imagem2 com o lado direito do widget Espaço e o lado superior da imagem2 com o lado inferior do widget Espaço. Isso vinculará image2 ao widget Space e, como o widget Space é restrito de todos os lados, podemos definir a polarização horizontal ou vertical necessária que moverá a image2 conforme necessário.

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

Além disso, para posicionar a imagem2 na parte inferior central da imagem1, podemos restringir os lados esquerdo e direito da imagem2 com os lados esquerdo e direito do widget Espaço, respectivamente. Da mesma forma, podemos colocar image2 em qualquer lugar alterando as restrições de image2 com o widget Space.

Swati Navalkar
fonte
Obrigado pela resposta!
user3193413
11

Eu encontrei uma maneira de fazer isso muito mais simples.

Basicamente, tenha o ImageView e, em seguida, em Text View adicione a restrição superior para corresponder à restrição superior da imagem e apenas adicione a margem superior do TextView para corresponder para obter o comportamento do tipo de margem -ve.

vinculo
fonte
9
a menos que a altura da imagem seja variável
Stas Shusha
Ainda funcionaria. Você só precisa ter cuidado sobre onde deseja que o widget superior sobreponha a imagem, definir a margem da direita ou da parte inferior pode ser uma opção melhor, ou você pode usar o viés, todos respondendo à pergunta .
Gabriel Vasconcelos
4

Isso vai ajudar muitos

No meu caso, quero meu design assim:

depois de

Significa que eu quero que minha imagem exiba metade de sua largura, então basicamente preciso de uma margem negativa da metade da largura real da imagem, mas meu layout inteiro no layout de restrição e no layout de restrição não permite margem negativa, então consegui isso com o código abaixo

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

Assim, o ImageView terminará no início da diretriz. e o efeito é igual à margem negativa no início de 50 dp.

E também se a largura da sua visão não for fixa e estiver em porcentagem, para que você possa colocar a linha de orientação com porcentagem e obter o efeito que deseja

Happy Coding :)

Vijay Makwana
fonte
0

Você só precisa usar o widget Space em seu layout

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

Esta é uma pergunta antiga, mas muito questionada, a maneira mais rápida de conseguir isso é restringindo a parte superior e inferior do lado da vista que você deseja ancorar, assim:

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

Isso o centralizará na linha inferior da visualização, centralizado horizontalmente.

Ahmed Awad
fonte
-1

Uma maneira simples.

Não tenho certeza da melhor maneira.

Apenas envolva 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>
최봉재
fonte
Isso adiciona um pouco mais de aninhamento, MAS funciona para animar visualizações de cima, ao contrário das outras soluções aqui, que presumem que a visualização será exibida completamente. Obrigado!
Leo torce para Monica Cellio
2
Essa resposta precisa de muito mais informações. Não está claro como colocar algo em um LinearLayout pode ser usado para realizar a sobreposição.
Robert Liberatore