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