Bagaimana cara mencapai overlap / margin negatif pada Constraint Layout?

118

Apakah mungkin untuk mencapai margin negatif pada tata letak batasan untuk mencapai tumpang tindih? Saya mencoba untuk memiliki gambar yang berpusat pada tata letak dan memiliki tampilan Teks sedemikian rupa sehingga tumpang tindih dengan x dp. Saya mencoba menetapkan nilai margin negatif tetapi tidak berhasil. Akan sangat bagus jika ada cara untuk mencapai ini.

obligasi
sumber
Panduan mungkin membantu, saya belum mencoba.
Eugen Pechanec

Jawaban:

214

Klarifikasi: Jawaban di bawah ini tetap valid, tetapi saya ingin mengklarifikasi beberapa hal. Solusi asli akan menempatkan tampilan dengan ofset negatif de facto sehubungan dengan tampilan lain seperti yang dinyatakan dan akan muncul dalam tata letak seperti yang ditunjukkan.

Solusi lain adalah dengan menggunakan properti translationY seperti yang disarankan oleh Amir Khorsandi di sini . Saya lebih suka solusi yang lebih sederhana dengan satu peringatan: Terjemahan terjadi pasca-tata letak , sehingga tampilan yang dibatasi ke tampilan yang dipindahkan tidak akan mengikuti terjemahan.

Misalnya, XML berikut menampilkan dua TextView tepat di bawah gambar. Setiap tampilan dibatasi dari atas ke bawah dengan tampilan yang muncul tepat di atasnya.

masukkan deskripsi gambar di sini

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

Sekarang, mari kita menerjemahkan "Katakanlah nama saya" TextView oleh 50dpdengan menentukan

android:translationY="-50dp"

Ini menghasilkan yang berikut:

masukkan deskripsi gambar di sini

TextView "Say my name" telah bergeser ke atas seperti yang diharapkan, tetapi TextView "Say it" tidak mengikuti seperti yang kita harapkan. Ini karena terjemahan terjadi setelah tata letak . Meskipun tampilan berpindah tata letak pasca, itu masih bisa dibuat dapat diklik di posisi baru.

Jadi, IMO, gunakan translationX dan translationY untuk margin negatif di ConstraintLayout jika peringatan di atas tidak memengaruhi tata letak Anda; jika tidak, gunakan widget luar angkasa seperti yang dijelaskan di bawah ini.


Jawaban asli

Meskipun tampaknya margin negatif tidak akan didukung ConstraintLayout, ada cara untuk mencapai efek menggunakan alat yang tersedia dan didukung. Berikut adalah gambar di mana judul gambar tumpang tindih 22dpdari bagian bawah gambar - secara efektif menjadi -22dpmargin:

masukkan deskripsi gambar di sini

Ini dilakukan dengan menggunakan Spacewidget dengan margin bawah sama dengan offset yang Anda inginkan. The Spacewidget kemudian memiliki bagian bawahnya dibatasi untuk bagian bawah ImageView. Sekarang yang perlu Anda lakukan adalah membatasi bagian atas TextViewdengan judul gambar ke bagian bawah Spacewidget. Ini TextViewakan diposisikan di bagian bawahSpace tampilan dengan mengabaikan margin yang telah ditetapkan.

Berikut ini adalah XML yang menyelesaikan efek ini. Saya akan mencatat bahwa saya menggunakan Spacekarena ringan dan ditujukan untuk jenis penggunaan ini, tetapi saya dapat menggunakan jenis lain Viewdan membuatnya tidak terlihat. (Anda mungkin perlu melakukan penyesuaian.) Anda juga dapat menentukan a Viewdengan margin nol dan tinggi margin sisipan yang Anda inginkan, dan membatasi bagian atas TextViewke atas inset View.

Namun pendekatan lain akan melapisi bagian TextViewatas ImageViewdengan menyelaraskan bagian atas / bawah / kiri / kanan dan membuat penyesuaian yang sesuai pada margin / padding. Manfaat dari pendekatan yang ditunjukkan di bawah ini adalah bahwa margin negatif dapat dibuat tanpa banyak perhitungan. Itu semua untuk mengatakan bahwa ada beberapa cara untuk mendekati ini.

Pembaruan: Untuk diskusi singkat dan demo teknik ini, lihat posting blog Google Developers Medium .

Margin Negatif untuk 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
sumber
Tidak berfungsi dalam kasus saya, ImageViewberpusat pada orang tua, TextViewtumpang tindih di atas ImageView. Kemudian Spacemembuat kesalahan saat Aplikasi kembali dari latar belakang. Saya pikir 0dp, 0dp membuat masalah. Bagaimana dengan penggunaan saja Guideline.
ilusi
Cara ini tidak dapat berfungsi karena tampilan memiliki batasan dengan induk.
R4j
Mengapa saya membutuhkan Space? Saya mencoba tanpa ruang dan saya mendapatkan hasil yang sama.
kuzdu
@kuzdu intinya di sini adalah bahwa bagian bawah ruang dibatasi di bagian bawah imageView, margin memindahkan ruang ke atas, lalu bagian atas tampilan teks dibatasi di bagian bawah ruang yang menciptakan "overlay setengah ini" "efek yang kami coba capai. Silakan posting jawaban di sini jika Anda berhasil mendapatkannya tanpa spasi sehingga kami dapat melihat kendala yang Anda gunakan pada dua tampilan Anda.
Lucas P.
Anda Heisenberg ...
Nahro
62

Cara lain adalah menggunakan translationXatau translationYseperti ini:

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

itu akan bekerja seperti android:layout_marginRight="-25dp"

Amir Khorsandi
sumber
29
Sadarilah ini, karena posisi sebenarnya dari tampilan tetap pada posisi semula (sebelum terjemahan), itu hanya akan diterjemahkan secara visual. Karenanya, peristiwa onClick hanya akan dipicu dari dalam posisi lama.
goemic
Pernyataan @ goemic sepertinya salah, karena ini bukan animasi tween tapi animasi properti. (tolong perbaiki saya jika saya salah)
Henry
praktik buruk karena tidak mendukung tata letak RTL
Salam El-Banna
27

Margin negatif tidak pernah didukung secara resmi di RelativeLayout. Margin negatif tidak akan didukung di ConstraintLayout. [...]

- Romain Guy pada 8 Jun 2016

Ikuti dua masalah ini:

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

Eugen Pechanec
sumber
Ide mereka mungkin bahwa dengan batasan tata letak Anda tidak memerlukan margin negatif. Itu mungkin benar jika Anda memiliki semua elemen dalam satu tata letak. Kadang-kadang meskipun Anda ingin mengelompokkan elemen secara logis seperti dalam baris perintah yang terbuat dari tombol, yang memiliki kelebihan sendiri untuk penggunaan kembali dan kejelasan dan enkapsulasi. Pada titik itu grup akan memiliki bentuk persegi panjang, dan batasan pada bentuk inilah yang membuat margin negatif berguna dan perlu lagi.
AndrewBloom
Margin negatif meniru konsep yang persis sama dari garis dasar pada teks, menjadi teks dalam bentuk kompleks dalam wadah persegi panjang
AndrewBloom
14

Inilah yang saya temukan setelah berjam-jam mencoba menemukan solusi.

Mari kita pertimbangkan dua gambar, gambar1 dan gambar2. Image2 harus ditempatkan di atas image1 yang diposisikan ke sisi kanan bawah.

Contoh Tampilan Tumpang Tindih gambar

Kita bisa menggunakan Space widget untuk tampilan yang tumpang tindih.

Batasi empat sisi widget Space dengan empat sisi image1 masing-masing. Untuk contoh ini, batasi sisi kiri gambar2 dengan sisi kanan widget Space dan sisi atas gambar2 dengan sisi bawah widget Space. Ini akan mengikat image2 dengan widget Space dan karena widget Space dibatasi dari semua sisi, kita dapat menentukan bias horizontal atau vertikal yang diperlukan yang akan memindahkan image2 sesuai kebutuhan.

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

Selain itu, untuk memposisikan gambar2 di tengah-bawah gambar1, kita dapat membatasi sisi kiri dan kanan gambar2 dengan sisi kiri dan kanan widget Space. Demikian pula, kita dapat menempatkan gambar2 di mana saja dengan mengubah batasan gambar2 dengan widget Space.

Swati Navalkar
sumber
Terima kasih atas jawabannya!
user3193413
11

Saya menemukan cara untuk melakukannya lebih sederhana.

Pada dasarnya memiliki ImageView, kemudian pada Text View tambahkan batasan teratas agar sesuai dengan batasan teratas gambar dan cukup tambahkan margin atas TextView agar sesuai untuk mencapai perilaku tipe margin -ve.

obligasi
sumber
9
kecuali tinggi gambar bervariasi
Stas Shusha
Itu masih akan berhasil. Anda hanya perlu berhati-hati tentang di mana Anda ingin widget atas Anda melapisi gambar, mengatur margin dari kanan atau dari bawah bisa menjadi opsi yang lebih baik, atau Anda bisa menggunakan bias, yang semuanya menjawab pertanyaan itu. .
Gabriel Vasconcelos
4

Ini akan membantu banyak orang

Dalam kasus saya, saya ingin desain saya seperti ini:

setelah

Berarti saya ingin gambar saya ditampilkan setengah dari lebarnya sehingga pada dasarnya saya membutuhkan margin negatif setengah dari lebar gambar sebenarnya tetapi seluruh tata letak saya dalam tata letak batasan dan tata letak batasan tidak mengizinkan margin negatif jadi saya mencapai ini dengan kode di bawah ini

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

Sehingga ImageView akan berakhir di awal pedoman. dan efeknya sama seperti margin negatif di awal 50dp.

Dan juga jika lebar tampilan Anda tidak tetap dan dalam persentase sehingga Anda dapat menempatkan pedoman dengan persentase dan mencapai efek apa pun yang Anda inginkan

Selamat Coding :)

Vijay Makwana
sumber
0

Anda hanya perlu menggunakan widget Space di tata letak Anda

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

Ini adalah pertanyaan lama namun sangat banyak ditanyakan, cara tercepat untuk mencapai ini adalah dengan membatasi bagian atas dan bawah ke sisi tampilan yang ingin Anda jangkar, seperti:

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

Ini akan memusatkannya di garis bawah tampilan, di tengah secara horizontal.

Ahmed Awad
sumber
-1

Cara yang Sederhana.

Saya tidak yakin cara terbaik.

Cukup bungkus menggunakan 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>
최봉재
sumber
Ini menambahkan sedikit lebih bersarang, TAPI berfungsi untuk menganimasikan tampilan dari atas, tidak seperti solusi lain di sini, yang mengasumsikan bahwa Tampilan akan ditampilkan sepenuhnya. Terima kasih!
Leo mendukung Monica Cellio
2
Jawaban ini membutuhkan lebih banyak informasi. Tidak jelas bagaimana meletakkan sesuatu di LinearLayout dapat digunakan untuk menyelesaikan tumpang tindih.
Robert Liberatore