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

是否有可能在约束布局上实现负裕度以实现重叠? 我试图有一个以布局为中心的图像,并有一个文本视图,使其重叠的 x dp。我试过设置负的边际价值,但没有运气。 如果有办法做到这一点,那就太好了。

79945 次浏览

在 RelativeLayout 中从未正式支持过负边距

—— Romain Guy on Jun 8,20162016年6月8日

关注以下两个问题:

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"

利润为负25个百分点。(这只有在视图顶部受到约束时才能工作。如果边距的侧面不受限制,则边距在 约束布局中没有作用。)



澄清: 下面的答案仍然有效,但是我想澄清一些事情。原始的解决方案将放置一个视图与另一个视图的 事实上负偏移量,如上所述,并将在布局中显示。

另一个解决方案是使用 Amir Khorsandi 给你建议的 翻译属性。我更喜欢这种简单的解决方案,但有一点需要注意: 转换发生在 布局后,因此受限于被替换视图的视图将不会跟随转换。

例如,下面的 XML 在图像的正下方显示两个 文本视图。每个视图都受到自上而下的约束,视图就出现在视图的正上方。

enter image description here

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

现在,让我们通过指定

android:translationY="-50dp"

这产生了以下结果:

enter image description here

“说出我的名字”文本视图已经如我们预期的那样向上移动,但是“说出来”文本视图并没有像我们预期的那样跟进。这是因为翻译发生在 布局后。尽管视图在布局之后移动,但仍然可以在新位置中单击它。

所以,我的意思是,如果上面的警告不会影响你的布局,那么在 约束布局中使用 翻译 X翻译的负边距; 否则,使用下面概述的 空间小部件。

另一个警告: 正如 Salam El-Banna 在对另一个答案的评论中所说的那样,翻译 X对于 RTL 布局来说不是一个好的解决方案,因为无论布局的 RTL 或 LTR 性质如何,翻译的标志都会指示移动的方向(左/右)。


原始答案

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

enter image description here

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

下面是实现此效果的 XML。我将注意到,我使用 Space是因为它是轻量级的,并且用于这种类型的使用,但是我可以使用另一种类型的 View并使其不可见。(不过,您可能需要做出一些调整。)您还可以定义一个边距为零的 View和所需的插入边距高度,并将 TextView的顶部约束为插入 View的顶部。

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

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

ConstraintLayout XML 的负边距

<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 的边距顶部来匹配来实现-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"一样工作

这是我花了几个小时想出来的解决办法。

让我们考虑两个图像,image 1和 image 2。将 Image2放置在 Image1的顶部,定位在右下角。

重叠视图示例 image

我们可以对重叠视图使用 太空小部件。

分别用图像1的四条边约束 Space 小部件的四条边。对于这个示例,用 Space 小部件的右侧约束 image 2的左侧,用 Space 小部件的底部约束 image 2的顶部。这将把 image 2与 Space 小部件绑定在一起,由于 Space 小部件受到所有方面的限制,我们可以定义所需的水平或垂直偏差,这将根据需要移动 image 2。

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

此外,为了将 image 2放置在 image 1的中心底部,我们可以分别使用 Space 小部件的左右两侧来约束 image 2的左右两侧。类似地,我们可以通过使用 Space 小部件更改 image 2的约束来将 image 2放置在任何地方。

这将有助于许多

在我的情况下,我希望我的设计是这样的:

after

意思是我想我的图像是显示宽度的一半,所以基本上我需要实际图像宽度的一半负边距,但我的整个布局在约束布局和约束布局不允许负边距,所以我实现了以下代码

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

布局是这样的。 enter image description here

这就是我的解决方案

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

在你的情况下,使用翻译 X 和翻译 Y 可能会有帮助。

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