如何在约束布局上实现重叠/负边距?

Posted

技术标签:

【中文标题】如何在约束布局上实现重叠/负边距?【英文标题】:How to achieve overlap/negative margin on Constraint Layout? 【发布时间】:2017-08-16 12:44:04 【问题描述】:

是否可以在约束布局上实现负边距以实现重叠? 我正在尝试使图像以布局为中心并具有文本视图,使其与 x dp 重叠。我尝试设置负边距值但没有运气。 如果有办法实现这一点,那就太好了。

【问题讨论】:

Guideline 可能有帮助,我没试过。 【参考方案1】:

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

android:layout_marginTop="-25dp"

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



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

另一种解决方案是使用 Amir Khorsandi here 建议的 translationY 属性。我更喜欢该解决方案,但要注意一点:翻译发生在布局后,因此受限于位移视图的视图不会跟随翻译。

例如,以下 XML 在图像下方显示两个 TextView。每个视图都受到从上到下的约束,视图紧邻其上方。

<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_
    android:layout_>

    <ImageView
        android:id="@+id/imageView"
        android:layout_
        android:layout_
        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_
        android:layout_
        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_
        android:layout_
        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 并没有像我们预期的那样跟随它。这是因为翻译发生在布局后。尽管视图在布局后移动,但仍可以在新位置使其可点击。

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

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


原答案

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

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

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

另一种方法是通过对齐顶部/底部/左侧/右侧并将TextView 覆盖在ImageView 的顶部,并对边距/填充进行适当的调整。下面演示的方法的好处是无需大量计算即可创建负边距。也就是说,有几种方法可以解决这个问题。

更新:有关此技术的快速讨论和演示,请参阅 Google Developers Medium blog post。

ConstraintLayout XML 的负边距

<android.support.constraint.ConstraintLayout
    android:layout_
    android:layout_>

    <ImageView
        android:id="@+id/imageView"
        android:layout_
        android:layout_
        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_
        android:layout_
        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_
        android:layout_
        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 以父级为中心,TextViewImageView 重叠。然后Space 在应用程序从后台返回时出错。我认为0dp,0dp会造成一些麻烦。不如直接使用Guideline 这种方式不适用于视图与父级有约束。 为什么需要空间?我试过没有空间,结果是一样的。 @kuzdu 这里的重点是空间的底部被约束在imageView的底部,margin将空间向上移动,然后是文本的top视图被限制在空间的底部,这会产生我们试图实现的这种“半覆盖”效果。如果您设法在没有空格的情况下获得答案,请在此处发布答案,以便我们可以看到您在两个视图上使用的约束。 更新不起作用android:layout_marginTop="-25dp"。没有影响【参考方案2】:

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

  <ImageView
                android:layout_
                android:layout_ 
                android:translationX="25dp"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"/>

它将像android:layout_marginRight="-25dp"一样工作

【讨论】:

请注意,由于视图的实际位置停留在原始位置(翻译前),因此只会在视觉上进行翻译。因此,onClick 事件只会在旧位置内触发。 @goemic 的说法似乎是错误的,因为这不是补间动画而是属性动画。 (如有错误请指正) 不好的做法,因为它不支持 RTL 布局【参考方案3】:

RelativeLayout 从未正式支持负边距。 ConstraintLayout 不支持负边距。 [...]

-- 2016 年 6 月 8 日的 Romain Guy

关注这两个问题:

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

【讨论】:

他们的想法可能是使用 constraintlayout 你不需要负边距。如果您将所有元素都放在一个布局上,那可能是正确的。有时,尽管您想对元素进行逻辑分组,例如由按钮组成的命令条,这在可重用性、清晰性和封装性方面具有其自身的优势。那时,该组将具有一个矩形形状,正是对该形状的这种限制使负边距再次变得有用和必要。 负边距与文本基线的概念完全相同,即文本是矩形容器内的复杂形状【参考方案4】:

这是我经过数小时尝试寻找解决方案后得出的结论。

让我们考虑两个图像,image1 和 image2。 Image2 将放置在位于右下角的 image1 的顶部。

重叠视图示例

我们可以使用 Space 小部件来重叠视图。

分别用 image1 的四个边约束 Space 小部件的四个边。对于此示例,使用 Space 小部件的右侧限制 image2 的左侧,并使用 Space 小部件的底部限制 image2 的顶部。这会将 image2 与 Space 小部件联系起来,并且由于 Space 小部件受到所有方面的限制,我们可以定义所需的水平或垂直偏差,这将根据需要移动 image2。

<?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_
android:layout_
tools:context=".Player">
 <ImageView
     android:id="@+id/image1"
     android:layout_
     android:layout_
     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_
     android:layout_
     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_
     android:layout_
     android:src="@android:color/holo_green_light"
     app:layout_constraintStart_toEndOf="@+id/space"
     app:layout_constraintTop_toBottomOf="@+id/space" />
 </android.support.constraint.ConstraintLayout>

此外,为了将 image2 定位在 image1 的中心底部,我们可以分别用 Space 小部件的左侧和右侧约束 image2 的左侧和右侧。同样,我们可以通过使用 Space 小部件更改 image2 的约束来将 image2 放置在任何地方。

【讨论】:

【参考方案5】:

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

基本上有了ImageView,然后在TextView上添加top约束来匹配图片的top约束,只需要添加TextView的margin top来匹配就可以实现-ve margin类型的行为。

【讨论】:

除非图像高度可变 它仍然可以工作。您只需要注意您希望上部小部件覆盖图像的位置,从右侧或底部设置边距可能是更好的选择,或者您可以使用偏差,所有这些都可以回答问题.【参考方案6】:

这将帮助很多人

就我而言,我想要这样的设计:

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

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_
    android:layout_>

    <ImageView
        android:layout_
        android:layout_
        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_
        android:layout_
        android:orientation="vertical"
        app:layout_constraintGuide_begin="50dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

这样 ImageView 将在指南的开头结束。效果和50dp开头的负边距一样。

如果你的视图的宽度不是固定的,它是百分比,这样你就可以用百分比放置指南并达到你想要的任何效果

快乐编码:)

【讨论】:

【参考方案7】:

您只需要在布局中使用 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_
android:layout_>

<Space
    android:id="@+id/negative_margin"
    android:layout_
    android:layout_
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintRight_toLeftOf="parent"/>

<Button
    android:id="@+id/button"
    android:layout_
    android:layout_
    android:text="Widget who needs negative margin"
    app:layout_constraintTop_toBottomOf="@+id/negative_margin"
    app:layout_constraintLeft_toLeftOf="@+id/negative_margin" />

【讨论】:

【参考方案8】:

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

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

<View
   android:id="@+id/subjectBackground"
   android:layout_
   android:layout_
   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" />

【讨论】:

【参考方案9】:

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

        <androidx.appcompat.widget.AppCompatImageView
        android:layout_
        android:layout_
        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" />

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

【讨论】:

【参考方案10】:

大家可以试试这个方法,简单多了

 <?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_
    android:layout_
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".MyProfileFragment">

 
    <ImageView

        android:id="@+id/imageViewUserPic"
        android:layout_
        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_>

    </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_
        android:layout_>

    </ImageView>



</androidx.constraintlayout.widget.ConstraintLayout>

布局会是这样的..

【讨论】:

【参考方案11】:

这是我的解决方案

<com.oven.test.avatar
 android:id="@+id/imageview_a"
 android:layout_
 android:layout_
 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_
    android:layout_
    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"/>

【讨论】:

【参考方案12】:

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

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

【讨论】:

【参考方案13】:

一个简单的方法。

我不确定最好的方法。

只需使用 LinearLayout 进行包装

<LinearLayout
android:layout_
android:layout_
>
 <View
 android:layout_
 android:layout_marginLeft="-20dp"
 android:layout_/>
</LinearLayout>

【讨论】:

这增加了一点嵌套,但它适用于从顶部动画视图,不像这里的其他解决方案,它假设视图将被完全显示。谢谢! 这个答案需要更多信息。不清楚如何使用 LinearLayout 中的内容来实现重叠。

以上是关于如何在约束布局上实现重叠/负边距?的主要内容,如果未能解决你的问题,请参考以下文章

约束布局 TextView 重叠

如何为约束布局组提供垂直边距?

2 TextViews 在约束布局中相互重叠

在约束布局中的按钮上重叠图像视图

SwiftUI 列表中基于约束的图像布局

CSS布局奇淫巧计之-强大的负边距