在Android中使用负边距是一种不好的实践吗?

139

负边距示例:

                         在此输入图像描述

场景描述

通过给其中一个视图设置负边距,使其侵入另一个视图的边界框从而导致两个视图重叠。

想法

如果需要重叠布局,则使用负边距的方式似乎按预期工作。 但是,我不想因为无意中没有正确地处理事情而遇到更大的问题。 当您使用负边距时,无论是模拟器、物理设备还是其他任何什么东西都似乎会正常工作,一个视图侵入了另一个视图的边界框,具体取决于在布局中如何声明它将位于另一个视图的上方还是下方。

我还知道自API 21以来,我们可以设置translationZelevation属性,使视图显示在其他视图的上面或下面,但是我的关注点基本上来自于文档中针对layout_margin属性的说明明确指出边距值应该是正数,让我引用一下:

摘录:
指定此视图左侧、顶部、右侧和底部的额外空间。 这个空间位于这个视图的范围之外。边距值应该是正数。 必须是一个尺寸值,即一个浮点数并附加一个单位,如“14.5sp”。 可用单位有:px(像素),dp(密度无关像素),sp(根据首选字体大小缩放的比例像素),in(英寸),mm(毫米)...

在我最初提出这个问题后的几年中,我并没有遇到负边距的任何问题,尽可能地避免使用它们,但是没有遇到任何问题,所以即使文档中规定了这一点,我也不太担心。


2
我知道如果对象的边距之一为负数,Espresso测试将无法看到该对象...所以这是不使用它们的原因。 - Tim Boland
8个回答

216
2010年,核心Android工程师@RomainGuy表示负边距具有未指定的行为
2011年,@RomainGuy表示您可以在LinearLayoutRelativeLayout上使用负边距
2016年,@RomainGuy表示它们从未得到官方支持,并且不会被ConstraintLayout支持
2020年12月(v2.1.0,2021年6月正式发布),约束中添加了对负边距的支持
不过,绕过此限制很容易。
在您的基本视图底部添加一个辅助视图(高度为0dp,宽度约束为父级),在底部添加您想要的边距。
然后将您的视图定位在此视图下方,有效地允许其具有“负”边距,但无需使用任何不支持的负值。

2
@DrewLeSueur:我不会做出那样的假设。我甚至不知道负填充是什么意思。 - CommonsWare
3
抱歉,但是不支持该操作。 - CommonsWare
22
我注意到在安卓 4.4 KitKat 版本中(至少在华硕 Nexus 7 上),与负边距相关的东西发生了一些变化(与 4.3 相比)。结果证明,你需要添加 android:clipChildren="false"android:clipToPadding="false" 属性,而以前则不需要这么做,否则会导致类似 这样的问题 - Jonik
1
@Jonik 我认为你甚至不需要设置 android:clipChildren="false"。只需将 android:clipToPadding="false" 应用于父 ViewGroup 即可。 - jenzz
3
从ConstraintLayout 2.1版本开始,支持负边距。 https://developer.android.com/jetpack/androidx/releases/constraintlayout#constraintlayout-2.1.0-alpha2 - hustoj2
显示剩余11条评论

27
希望这能帮到某些人。以下是使用ConstraintLayout的可行代码示例,基于@CommonsWare的回答:

在您的基本视图底部添加一个帮助器视图(高度为0dp,宽度限制为父级),在底部添加所需的边距。然后将您的视图定位在此视图下方,有效地允许其具有“负”边距,但无需使用任何不受支持的负值。

示例代码:

<TextView
    android:id="@+id/below"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#F1B36D"
    android:padding="30dp"
    android:text="I'm below"
    android:textColor="#ffffff"
    android:textSize="48sp"
    android:textAlignment="center"
    tools:layout_editor_absoluteX="129dp"
    tools:layout_editor_absoluteY="0dp" />

<android.support.v4.widget.Space
    android:id="@+id/space"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginBottom="32dp"
    app:layout_constraintBottom_toBottomOf="@+id/below"
    app:layout_constraintLeft_toLeftOf="@id/below"
    app:layout_constraintRight_toRightOf="@id/below" />

<TextView
    android:id="@+id/top"
    android:layout_width="100dp"
    android:layout_height="60dp"
    android:textAlignment="center"
    android:textColor="#ffffff"
    android:text="I'M ON TOP!"
    android:background="#676563"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/space" />

输出:

在此输入图像描述


20

您可以使用 translationXtranslationY 来代替负边距。

例如:

android:layout_marginBottom = -2dp

android:translationY = -2dp

更新: 请注意,整个视图都已翻译。


17

如果您想使用负边距,请为容器及其clipToPadding设置足够的填充,并将其设置为false,然后为其子元素设置负边距,以便它不会剪裁子视图!


5

在过去,这可能是不好的做法,但随着Material Design和它的浮动操作按钮,现在在许多情况下似乎是不可避免和必需的。基本上,当您有两个单独的布局时,无法将它们放入一个单一的RelativeLayout中,因为它们需要明显分开处理(例如,考虑标题和内容),唯一的重叠FAB的方法是使用负边距使其从其中一个布局中突出。这会创建与可点击区域相关的其他问题。


3
对于设置TextView的负边距(我知道OP是指ViewGroup,但我在寻找设置负边距时遇到了问题,并在此处着陆)......我发现4.0.3(API 15)存在问题,只有当android:layout_marginTopandroid:layout_marginBottom设置为负值(如-2dp)时才会出现该问题。由于某种原因,TextView根本不显示。它似乎从视图中消失了(而不仅仅是不可见)。当我使用其他3个版本的layout_margin时,没有看到这个问题。请注意,我没有在实际设备上尝试过这一点,这是使用4.0.3模拟器进行的。这是我发现的第二个奇怪的只影响4.0.3的问题,所以我的新规则是始终使用4.0.3模拟器进行测试 :) 我成功地通过使用android:lineSpacingExtra="-2dp"来减小TextView的底部边距,即使我恰好设置了android:singleLine="true"(因此我不认为行间距是一个因素)。

1
我在Nexus 4(xhdpi)和4.2.2上发现了类似的行为。有一个没有填充的布局,尽管父布局有填充。里面有一个带有负marginTop的TextView。在5.0上它可以正常工作。在4.2.2上,在设备和Nexus 4模拟器中都会消失。解决方案是将填充移动到包含TextView的布局中。 - louielouie

3
不,你不应该使用“负边距”,而应该使用“translate”。即使有时候负边距也可以工作,但当你通过编程方式更改布局时,“translate”会更好。并且在使用边距时,视图不会溢出屏幕。

0

我只知道这是可能的一个相当短的时间。但我认为这没有问题。只需注意屏幕尺寸等,以确保不会意外地使不应该重叠出现的项目。(例如,文本叠在文本上可能是个坏主意。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接