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


117

是否可以在约束布局上实现负余量以实现重叠?我正在尝试使图像位于布局中心,并具有“文本”视图,使其与x dp重叠。我尝试设置负保证金值,但没有运气。如果有一种方法可以实现这一点,那就太好了。


指南可能会有所帮助,我没有尝试过。
Eugen Pechanec

Answers:


214

澄清:以下答案仍然有效,但我想澄清两点。原始解决方案将相对于另一个视图放置一个实际上具有负偏移的视图,并将其显示在所示的布局中。

另一个解决方案是使用Amir Khorsandi 此处建议的translationY属性。我希望该解决方案更简单,但有一个警告:平移是在布局后进行的,因此受限于位移视图的视图将不跟随平移。

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

在此处输入图片说明

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

现在,让我们来翻译“说我的名字” 的TextView50dp指定

android:translationY="-50dp"

这将产生以下结果:

在此处输入图片说明

在“说我的名字” 的TextView已经上移的预期,但“说” 的TextView没有遵循它,因为我们所期望的。这是因为翻译发生在布局后。尽管视图在布局后移动,但仍可以在新位置将其单击。

因此,如果上面的注意事项不影响您的布局,那么IMO,请在ConstraintLayout中使用translationXtranslationY获得负边距。否则,请按照以下概述使用空间小部件。


原始答案

尽管似乎没有支持负边距的功能ConstraintLayout,但仍有一种方法可以使用可用和受支持的工具来实现效果。这是图像标题与图像22dp底部重叠的图像-有效地是-22dp空白:

在此处输入图片说明

这是通过使用Space底部边距等于所需偏移量的小部件来完成的。Space然后,将小部件的底部限制为的底部ImageView。现在,您需要做的就是将顶部TextView的图像标题限制在Space小部件的底部。的TextView将被定位在所述底部Space忽略所设定的余量视图。

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

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

更新:有关此技术的快速讨论和演示,请参阅Google Developers Medium 博客文章

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上方重叠ImageView。然后Space,当应用程序从后台返回时,会产生错误。我认为0dp,0dp会带来一些麻烦。怎么用呢Guideline
illusionJJ

这种方法不适用于视图受父对象约束的情况。
R4j

为什么需要一个空间?我试着没有空间,结果也一样。
kuzdu

@kuzdu这里的要点是,空间的底部被限制在imageView的底部,边距将空间向上移动,然后文本视图的顶部被限制在空间的底部,从而创建了这种“半覆盖我们正在努力达到的效果。如果您设法在没有空格的情况下获得答案,请在此处发布答案,以便我们可以看到您在两个视图上使用的约束。
Lucas P.

你是海森堡...
娜罗

62

另一种方法是使用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"


29
请注意,由于视图的实际位置保持在原始位置(平移之前),因此只能以视觉方式进行平移。因此,onClick事件将仅从旧位置内触发。
goemic

@goemic的声明似乎是错误的,因为这不是补间动画而是属性动画。(如果我错了,请纠正我)
亨利

不良做法,因为它不支持RTL布局
Salam El-Banna

27

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

-罗曼·盖伊(Romain Guy)2016年6月8日

请遵循以下两个问题:

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


他们的想法可能是约束约束您不需要负边距。如果所有元素都放在一个布局上,那可能是正确的。有时,尽管您希望将逻辑元素分组,例如在由按钮制成的命令条中,但在可重用性,清晰度和封装性方面有其自身的优势。到那时,该组将具有矩形形状,正是这种形状上的限制使得负边距变得有用且再次必要。
AndrewBloom

负边距在文本上模拟与基线基本相同的概念,即文本在矩形容器中为复杂形状
AndrewBloom

14

经过数小时的寻找解决方案,我才明白这一点。

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

重叠视图示例 图片

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

将Space小部件的四个面分别限制为image1的四个面。在此示例中,将image2的左侧限制为Space小部件的右侧,将image2的顶部限制为Space小部件的底部。这会将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_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>

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


谢谢您的回答!
user3193413

11

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

基本上有ImageView,然后在Text View上添加top约束以匹配图像的top约束,并仅添加TextView的margin top进行匹配以实现-ve margin类型的行为。


9
除非图像高度是可变的
斯塔斯舒沙

它仍然可以工作。您只需要谨慎选择上方窗口小部件要覆盖图像的位置,从右侧或底部设置边距可能是一个更好的选择,或者您可以使用偏差,所有这些都可以回答问题。
加布里埃尔·瓦斯科塞洛斯

4

这将帮助许多人

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

后

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

<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开始时的负余量相同。

另外,如果视图的宽度不是固定的,而是以百分比为单位的,那么您可以按百分比放置准则,并实现所需的效果

快乐编码:)


0

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


0

这是一个古老的问题,但仍然存在很多问题,实现此目标的最快方法是将视图的顶部和底部限制在要锚定的视图的侧面,如下所示:

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

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


-1

一个简单的方法。

我不确定最好的方法。

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

这增加了更多的嵌套,但是它可以使视图从顶部动画化,这与此处假定视图将被完全显示的其他解决方案不同。谢谢!
Leo支持Monica Cellio

2
该答案需要更多信息。不清楚如何将某些东西放入LinearLayout中以完成重叠。
罗伯特·利伯托雷特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.