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

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

【问题讨论】:

  • Guideline 可能有帮助,我没试过。

标签: android android-constraintlayout constraint-layout-chains


【解决方案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_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>

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

另一种方法是像这样使用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"一样工作

【讨论】:

  • 请注意,由于视图的实际位置停留在原始位置(翻译前),因此只会在视觉上进行翻译。因此,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_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 放置在任何地方。

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

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

    这将帮助很多人

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

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

    <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开头的负边距一样。

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

    快乐编码:)

    【讨论】:

      【解决方案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_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" />
      

      【讨论】:

        【解决方案8】:

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

        我想使用负边距在主题视图后面添加一个视图,该视图按比例大于主题视图。我找到的解决方案是缩放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" />
        

        【讨论】:

          【解决方案9】:

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

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

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

          【讨论】:

            【解决方案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_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>
            

            布局会是这样的..

            【讨论】:

              【解决方案11】:

              这是我的解决方案

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

              【讨论】:

                【解决方案12】:

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

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

                【讨论】:

                  【解决方案13】:

                  一个简单的方法。

                  我不确定最好的方法。

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

                  【讨论】:

                  • 这增加了一点嵌套,但它适用于从顶部动画视图,不像这里的其他解决方案,它假设视图将被完全显示。谢谢!
                  • 这个答案需要更多信息。不清楚如何使用 LinearLayout 中的内容来实现重叠。
                  猜你喜欢
                  • 1970-01-01
                  • 2020-02-05
                  • 2018-09-03
                  • 1970-01-01
                  • 2018-03-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-01-10
                  • 1970-01-01
                  相关资源
                  最近更新 更多