如何在约束布局上实现 overlap/negative 边距?

How to achieve overlap/negative margin on Constraint Layout?

是否可以在constraint layout上实现negative margin实现overlap? 我试图让图像以布局为中心,并有一个文本视图,使其与 x dp 重叠。我尝试设置负保证金值但没有运气。 如果有办法实现就太好了。

Negative margins have never been officially supported in RelativeLayout. Negative margins will not be supported in ConstraintLayout. [...]

-- Romain Guy on Jun 8, 2016

关注这两个问题:

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

更新 ConstraintLayout 现在支持版本 2.1.0-alpha2 的负边距。简单地说

android:layout_marginTop="-25dp"

对于负 25dp 边距。 (这仅在视图的顶部受到约束时有效。如果边距的一侧不受约束,则边距在 ConstraintLayout 中无效。)



澄清: 下面的答案仍然有效,但我想澄清几件事。原始解决方案将放置一个视图,该视图相对于所述另一个视图具有 事实上 负偏移,并将如图所示显示在布局中。

另一个解决方案是使用 Amir Khorsandi here 建议的 translationY 属性。我更喜欢该解决方案,因为有一个警告:翻译发生 post-layout,因此受限于移位视图的视图将不会遵循翻译。

例如,下面的 XML 在图像正下方显示两个 TextViews。每个视图都受限于 top-to-bottom 紧邻其上方的视图。

<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 已按预期向上移动,但“说出它”TextView 没有跟进正如我们所料。这是因为翻译发生 post-layout。虽然视图移动了 post-layout,但在新位置仍然可以点击它。

所以,IMO,如果 ConstraintLayout 中的负边距,请使用 translationXtranslationY 如果上面的警告不会影响您的布局;否则,请使用 space 小部件,如下所述。

另一个警告: 正如 Salam El-Banna 在对另一个答案的评论中所说,translationX 将不是一个好的RTL 布局的解决方案,因为无论布局的 RTL 或 LTR 性质如何,翻译的符号将决定移位的方向 (left/right)。


原回答

虽然 ConstraintLayout 似乎不支持负边距,但有一种方法可以使用可用和受支持的工具来实现效果。这是一张图片,其中图片标题与图片底部重叠 22dp - 实际上是 -22dp 边距:

这是通过使用 Space 小部件实现的,该小部件的底部边距等于您想要的偏移量。 Space 小部件然后将其底部限制在 ImageView 的底部。现在您需要做的就是将带有图像标题的 TextView 的顶部限制在 Space 小部件的底部。 TextView 将位于 Space 视图的底部,忽略设置的边距。

下面是实现这个效果的XML。我会注意到我使用 Space 因为它是轻量级的并且适合这种类型的使用,但我可以使用另一种类型的 View 并使其不可见。 (不过,您可能需要进行调整。)您还可以定义一个 View 具有零边距和所需的插入边距高度,并将 TextView 的顶部限制在插图 View.

另一种方法是通过对齐 tops/bottoms/lefts/right 将 TextView 覆盖在 ImageView 之上,并对 margins/padding 进行适当的调整。下面展示的方法的好处是无需大量计算即可创建负边距。这就是说有几种方法可以解决这个问题。

更新: 有关此技术的快速讨论和演示,请参阅 Google 开发人员媒体 blog post

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>

我找到了一种更简单的方法。

基本上有ImageView,然后在TextView上添加顶部约束以匹配图像的顶部约束并且只需添加TextView的边距顶部以匹配以实现-ve边距类型行为。

一个简单的方法。

我不确定最好的方法。

只需使用 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>

另一种方法是像这样使用 translationXtranslationY

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

一样工作

This is what I figured out after hours of trying to find a solution.

Let us consider two images, image1 and image2. Image2 is to be placed on top of image1 positioned to the bottom-right side.

Overlapping Views Example

We can use Space widget for overlapping views.

Constraint the Space widget's four sides with the four sides of the image1 respectively. For this example, constraint the image2's left side with the Space widget's right side and the image2's top side with the Space widget's bottom side. This will tie image2 with the Space widget and since the Space widget is constrained from all the sides, we can define required horizontal or vertical bias which will move image2 as required.

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

Additionally, to position image2 on the center-bottom of image1, we can constraint image2's left and right sides with Space widget's left and right sides respectively. Similarly, we can place image2 anywhere by changing image2's constraints with Space widget.

这会帮助很多人

在我的例子中,我希望我的设计是这样的:

意味着我希望我的图像显示其宽度的一半所以基本上我需要实际图像宽度一半的负边距但是我在约束布局和约束布局中的整个布局不允许负边距所以我用下面的代码实现了这个

<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开头的负边距效果一样。

而且如果你的视图的宽度不是固定的并且它是百分比,那么你可以用百分比放置指南并实现你想要的任何效果

快乐编码:)

您只需在布局中使用 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" />

这是一个老问题,但被问得非常多,最快的方法是将顶部和底部限制在要锚定的视图的一侧,如下所示:

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

这将使它在视图的底部居中,水平居中。

将背景视图放在主题视图后面

我想使用负边距在主题视图后面添加一个视图,该视图按比例大于主题视图。我找到的解决方案是缩放 android:scaleX="1.2"android:scaleY="1.2" 背景视图,同时将其限制在主题的各个方面。

<View
   android:id="@+id/subjectBackground"
   android:layout_width="0dp"
   android:layout_height="0dp"
   android:scaleY="1.2"
   android:scaleX="1.2"
   app:layout_constraintBottom_toBottomOf="@+id/subjectView"
   app:layout_constraintEnd_toEndOf="@+id/subjectView"
   app:layout_constraintStart_toStartOf="@+id/subjectView"
   app:layout_constraintTop_toTopOf="@+id/subjectView" />

可以这样试试,这样简单多了

 <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".MyProfileFragment">

 
    <ImageView

        android:id="@+id/imageViewUserPic"
        android:layout_width="@dimen/dp60"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_margin="20dp"
        android:layout_height="wrap_content">

    </ImageView>


    <ImageView

        android:id="@+id/imageViewEdit"
        app:layout_constraintBottom_toBottomOf="@+id/imageViewUserPic"
        android:src="@drawable/ic_edit_red_round"
        app:layout_constraintEnd_toEndOf="@+id/imageViewUserPic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    </ImageView>



</androidx.constraintlayout.widget.ConstraintLayout>

布局将是这样的..

这是我的解决方案

<com.oven.test.avatar
 android:id="@+id/imageview_a"
 android:layout_width="128dp"
 android:layout_height="128dp"
 android:layout_marginTop="28dp"
 app:layout_constraintStart_toStartOf="parent"
 app:layout_constraintEnd_toEndOf="parent"
 app:layout_constraintTop_toTopOf="parent"/>

<com.oven.test.smallicon
    android:id="@+id/small_icon_overlap_a"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="30dp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintBottom_toBottomOf="@+id/imageview_a"
    app:layout_constraintTop_toTopOf="@+id/imageview_a"
    app:layout_constraintVertical_bias="1"
    android:layout_marginBottom="20dp"/>

使用 translationX 和 translationY 可能适合您的情况。

<TextView
    android:id="@+id/tvText"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="text"
    android:translationX="-15dp"
    android:translationY="10dp"
    app:layout_constraintEnd_toEndOf="@+id/imageView"
    app:layout_constraintTop_toTopOf="@+id/imageView" />