ConstraintLayout-那些很有用但是你可能不知道的用法

ConstraintLayout-那些很有用但是你可能不知道的用法

前沿

本篇不讲ConstraintLayout的常规用法,会讲一些约束布局很用的,但是可能你并不知道的用法

1、(简单开胃菜)B相对于A底部居中对齐

在这里插入图片描述
如上图所示:需要B布局相对于A布局的底部垂直居中对齐,水平居中对齐

方案1 使用LinearLayout为外层布局

  1. 需要指定B一个固定高度
  2. 给定B一个负的向上的margin,值为B高度的一半
    代码和效果如下所示:

在这里插入图片描述

缺陷:很明显,如果只是使用XML布局的形式(不通过代码动态计算),B布局需要固定一个高度才行,顶部margin也是固定值,如果B布局高度可变,还是很不方便的。

方案2 使用RelativeLayout为外层布局

  1. 同样需要给B一个固定的高度值
  2. B的alignBottom设置为A布局
  3. 给B设置一个负的marginBotttom

缺陷:同使用LinearLayout作为父布局一样,如果只是使用XML布局的形式(不通过代码动态计算),B布局需要固定一个高度才行,margin也是固定值,如果B布局高度可变,还是很不方便的。

终极方案:使用ConstraintLayout

  1. 水平方向相对于父布局居中
  2. 关键是垂直方向
  3. B的顶部相对于A的底部
  4. B的底部相对于A的底部

代码和效果图如下图所示:

在这里插入图片描述

对,没看错,就是这么简单

优势 不用管B的高度是多高,B布局都会自动相对A的底部剧中对齐

2、goneMargin属性的妙用

在这里插入图片描述

如上图所示:最外层是约束布局,A距离顶部138dp,B在A的下面,距离A的顶部38dp

==问题:==当A为gone的时候,需要B也距离顶部138dp的距离,如何实现?

方案1: 提枪上马,直接干,当A为gone时候,直接代码设置B的marginTop为138dp

方案2: A 和B外面再套一层布局,这层布局距离顶部是138dp,当A隐藏时候,将B的marginTop设置为0

方案3: 这里就不例举了,大都数实现方法都是需要动态设置的,不灵活不方便,不丝滑

终极方案:使用goneMargin属性
1、goneMargin属性,这里是B的顶部在A的底部,故使用
app:layout_goneMarginTop="138dp"属性即可
当A布局显示的时候,A相距顶部是138dp,B相距A的顶部是38dp,这时候goneMarginTop属性并不会生效(顾名思义gon eMargin),效果如下图所示:
在这里插入图片描述

2、将A设置为gone,这时候B的marginTop属性将会失效,取而代之的是B的goneMarginTop属性,这样就达到我们的要求了,效果图如下所示:

在这里插入图片描述

优势: 这个属性在涉及一些需要隐藏部分视图,同时需要改变相对的margin,对齐等等属性的时候,是非常有用的。无需使用代码动态计算,无需其他的一些嵌套,你会了吗?

2、Group的使用

在这里插入图片描述

如上图所示:
有三个TextView,如果需要控制AB同时隐藏显示,如何实现?

常规方案 一般有两种思路,其一就是逐个设置布局的Visible属性,其二可以在AB外面再包一层布局,通过设置外层布局的显示隐藏属性。

弊端 很明显,一个个设置很麻烦,而在外面包一层布局的话就增加了布局的层次,会增加布局渲染的开销,这也是我们不希望看到的。

使用ConstraintLayout 的Group 方案

  1. 新建一个Group ,引用A,B的id,这样就讲AB分成一个组了,只需要设置组的显示隐藏属性即可

在这里插入图片描述

将group_ab的属性visibility属性设置成gone,这样TextA和TextB就可以同时隐藏了。如下图所示:在这里插入图片描述

3、圆弧定位
  • layout_constraintCircle
  • layout_constraintCircleAngle
  • layout_constraintCircleRadius

它的作用就是你可以相对于锚点View的中心位置,声明一个角度和距离(半径)来确定View的位置

  • ayout_constraintCircle 是关联的锚点View的id
  • layout_constraintCircleRadius View的中心点与关联的锚点View的中心点的距离(圆弧半径)
  • layout_constraintCircleAngle View的中心点与关联的锚点View的中心点的角度关系(0到360度)

官方示意图:

在这里插入图片描述

代码效果如下图所示:

在这里插入图片描述

4、真的会用0dp
  • ConstraintLayout相对于其他布局提供给了一个0dp的属性(MATCH_CONSTRAINT ),这不是说让宽或高位0dp,而是一种标记。

  • 它标记的含义会随着不同的constrain设置而有不同的体现。

  • 有些时候0dp和layout_constraintWidth_default一起使用,会有意想不到的效果在这里插入图片描述

  • layout_constraintWidth_default的三种取值

    1. 默认是spread,意思是占用所有的符合约束的空间
    2. percent, 顾名思义就是按照百分比来表示宽度
    3. wrap:匹配内容大小但不会超过约束的限制。(和指定宽度为wrap_content的区别是不会超过约束限制)

需求1
如下图,内容布局在A和B的中间
在这里插入图片描述
实现代码如下:
在这里插入图片描述
这个实现起来很简单,只要设置左右上下的约束就行了,同时设置android:layout_width=“0dp” 就行了,那么设置成android:layout_width=“wrap_content” 行不行呢?答案是不行的,当内容多的的时候,就会超出约束限制,变成下面的鸟样
在这里插入图片描述

0dp这里就是实现约束条件,让宽度不会超过约束

需求2 上面内容布局的居中,相信用过ConstraintLayout的都会,那么问题来了,如果需要给内容布局添加一个背景色,让那个背景色刚好包裹内容,如何实现呢?

如下图,我们直接加上背景色
在这里插入图片描述
显然这里是不符合要求的,我们需要背景色刚好包裹住内容,而不是显示在整个中间的空间,为什么会显示成这样呢?答案就是前面说的layout_constraintWidth_default默认是spread,占用所有的符合约束的空间也就是填充了中间所有的布局了,

实现 如果需要背景刚好包含内容,这里只需要将layout_constraintWidth_default设置成wrap就行了
如下图所示:
在这里插入图片描述
当内容较多时候,也是可以的,如下图所示:
在这里插入图片描述

杠精来了 我非要使用宽度的wrap_content来实现呢?
也是可以的,只要解决wrap_content内容过多时候超出约束的问题就行了,谷歌也是提供了属性的:app:layout_constrainedWidth=”true|false”
这样,使用wrap_content结合layout_constrainedWidth=true,也可以实现上面效果

布局效果图如下所示:在这里插入图片描述

注:以上都是使用的宽度进行演示的,对于高度也是一样的

5、 layout_constraintDimensionRatio

ConstraintLayout不仅支持宽高比例的配置,还能配置宽高比例根据其中的一个计算出另外一个的

当宽度高度有一个为0dp时,另外一个可以根据指定的ratio来确认自己的大小。

需求
如下图所示,A,B平分整个屏幕宽度,中间有3%的屏幕宽度作为间距,同时A,B的宽高比都是2:1,如何实现?

在这里插入图片描述

分析

  1. 首先将layout_width和layout_height都设置为0dp,以应用我们的具体的约束
  2. 由于A,B宽度相对于屏幕是固定的,即每一个宽度:(100-3)/2=48.5,故可以按百分比布局指定宽度
  3. A,B的宽高比都是2:1,故将约束加在高度上,指定宽高比h,2:1即可

布局效果图如下:

在这里插入图片描述

so easy!

6. Barrier (栅栏,屏障)的使用

Barrier 是用多个 View 作为限制源来决定自身位置的一种辅助线
将多个view 放入barrier中,barrier的边界随着view的改变而改变

如图所示:
在这里插入图片描述

这里我们需要限定C和A顶部对齐,同时C在A和B的右侧,我们只需要将A和B加入barrier ,同时指定barrierDirection为right,然后指定C在barrier的右边,和A顶部对齐

布局效果图如下所示:
在这里插入图片描述

上面是基于最新稳定版本的一些特性
截止目前,最新稳定版本:
implementation ‘androidx.constraintlayout:constraintlayout:1.1.3’

下面的一些特性基于最新的测试版本,当然,你要在项目中使用也是可以的
截止目前,最新测试版本:
implementation ‘androidx.constraintlayout:constraintlayout:2.0.0-beta6’

7. Layer的使用

Layer 可以看作是它引用的 view 的边界(可以理解为包含这些 view 的一个 ViewGroup,但是Layer并不是ViewGroup,Layer并不会增加 view 的层级)。另外Layer支持对里面的 view 一起做变换

需求 如下图所示:A,B,C在同一个约束布局中,如果需要不增加层级的前提下,给A,B,C作为一个整体添加一个背景色,怎么实现好呢?

在这里插入图片描述

使用Layer

  1. Layer不提供位置约束,也就是其他布局约束到layer是不起作用的
  2. Layer 相当于把引用的视图圈一起,可以进行设置背景色,padding等
  3. 给Layer添加动画,就是给里面的每一个视图添加动画,对于一些复杂的动画的场景还是比较有用的

这里我们讲A,B,C添加到一个Layer, 同时设置背景色和padding,如下图所示:就像是用一个ViewGroup把三个视图包起来一样,实际并没有增加一个层级

在这里插入图片描述

给Layer执行动画:放大,旋转360度,平移

    btn.setOnClickListener {
            val anim = ValueAnimator.ofFloat(0f, 360f)
            anim.addUpdateListener { animation ->
                val angle = animation.animatedValue as Float
                layer.rotation = angle
                layer.scaleX = 1 + (180 - Math.abs(angle - 180)) / 30f
                layer.scaleY = 1 + (180 - Math.abs(angle - 180)) / 30f

                var shift_x = 50 * Math.sin(Math.toRadians((angle * 5).toDouble())).toFloat()
                var shift_y = 50 * Math.sin(Math.toRadians((angle * 7).toDouble())).toFloat()
                layer.translationX = shift_x
                layer.translationY = shift_y
            }
            anim.duration = 4000
            anim.start()
        }

效果图如下所示:

在这里插入图片描述

8. Flow 的使用

Flow 是 VirtualLayout,Flow 可以像 Chain 那样帮助快速横向/纵向布局constraint_referenced_ids里面的元素。 通过flow_wrapMode可以指定具体的排列方式,有三种模式

  • wrap none : 简单地把constraint_referenced_ids里面的元素组成chain,即使空间不够
    在这里插入图片描述
  • wrap chain : 根据空间的大小和元素的大小组成一条或者多条 chain
    在这里插入图片描述
  • wrap aligned : wrap chain类似,但是会对齐
    在这里插入图片描述

实例: 如何实现下面的简单计算器布局

在这里插入图片描述

原始方案
这里原是方案,可以通过LinearLayout 加上weight属性,通过权重来计算,但是这样就需要嵌套几层布局,而且众所周知,LinearLayout的weight效率不是那么友好的,那如何不通过前套实现上面的布局效果呢?

使用虚拟布局Flow

  1. 将所有的数字和“/”,“.",“*”放入Flow的引用
      app:constraint_referenced_ids="tv_num_7,tv_num_8,tv_num_9,tv_num_4,tv_num_5,tv_num_6,tv_num_1,tv_num_2,tv_num_3,tv_num_0,tv_operator_div,tv_dot,tv_operator_times"

  1. 设置Flow的属性,如下所示
app:flow_horizontalGap="10dp"  //每个item水平间距
app:flow_maxElementsWrap="3" //一行包含3个元素
app:flow_verticalGap="10dp"  //每个item垂直间距
app:flow_wrapMode="aligned"  //对齐方式
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
 app:layout_constraintStart_toStartOf="parent"
  1. 由于Flow是虚拟布局,并没有占用布局层次,故对于Compute按钮,可以直接相对于Flow 的子元素就行布局
 <TextView
        android:id="@+id/opration"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#CCBCD4"
        android:gravity="center"
        android:text="Compute"
        android:textColor="@android:color/white"
        android:textSize="24sp"
       app:layout_constraintBottom_toBottomOf="@+id/tv_operator_times"
        app:layout_constraintEnd_toEndOf="@+id/tv_dot"
        app:layout_constraintStart_toStartOf="@+id/tv_operator_div"
        app:layout_constraintTop_toTopOf="@+id/tv_operator_times" />
      

最上面的计算结果布局“0”,可以相对于Flow进行布局,在Flow的上面,左右和Flow进行对齐,代码如下

    <TextView
        android:id="@+id/result"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#CCA9F4"
        android:gravity="right|center_vertical"
        android:paddingEnd="16dp"
        android:text="0"
        android:textColor="@android:color/white"
        android:textSize="58sp"
        app:layout_constraintBottom_toTopOf="@+id/flow"
        app:layout_constraintEnd_toEndOf="@+id/flow"
        app:layout_constraintStart_toStartOf="@+id/flow"
        app:layout_constraintTop_toTopOf="parent" />

这样,将使用Linear Layout的多层嵌套变成0层嵌套,而且布局清晰,简单方便明了
完整的布局效果图:
在这里插入图片描述

完整的布局代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:ignore="MissingConstraints">


    <androidx.constraintlayout.helper.widget.Flow
        android:id="@+id/flow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFCCCCC"
        android:padding="20dp"
        app:constraint_referenced_ids="tv_num_7,tv_num_8,tv_num_9,tv_num_4,tv_num_5,tv_num_6,tv_num_1,tv_num_2,tv_num_3,tv_num_0,tv_operator_div,tv_dot,tv_operator_times"
        app:flow_horizontalGap="10dp"
        app:flow_maxElementsWrap="3"
        app:flow_verticalGap="10dp"
        app:flow_wrapMode="aligned"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/tv_num_7"
        style="@style/text_style"
        android:text="7" />

    <TextView
        android:id="@+id/tv_num_8"
        style="@style/text_style"
        android:text="8" />

    <TextView
        android:id="@+id/tv_num_9"
        style="@style/text_style"
        android:text="9" />


    <TextView
        android:id="@+id/tv_num_4"
        style="@style/text_style"
        android:text="4" />

    <TextView
        android:id="@+id/tv_num_5"
        style="@style/text_style"
        android:text="5" />

    <TextView
        android:id="@+id/tv_num_6"
        style="@style/text_style"
        android:text="6" />


    <TextView
        android:id="@+id/tv_num_1"
        style="@style/text_style"
        android:text="1" />

    <TextView
        android:id="@+id/tv_num_2"
        style="@style/text_style"
        android:text="2" />

    <TextView
        android:id="@+id/tv_num_3"
        style="@style/text_style"
        android:text="3" />

    <TextView
        android:id="@+id/tv_num_0"
        style="@style/text_style"
        android:text="0" />

    <TextView
        android:id="@+id/tv_operator_div"
        style="@style/text_style"
        android:text="/"
        tools:layout_editor_absoluteX="156dp"
        tools:layout_editor_absoluteY="501dp" />

    <TextView
        android:id="@+id/tv_operator_times"
        style="@style/text_style"
        android:text="*" />

    <TextView
        android:id="@+id/tv_dot"
        style="@style/text_style"
        android:text="."
        tools:layout_editor_absoluteX="278dp"
        tools:layout_editor_absoluteY="501dp" />

    <TextView
        android:id="@+id/opration"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#CCBCD4"
        android:gravity="center"
        android:text="Compute"
        android:textColor="@android:color/white"
        android:textSize="24sp"
        app:layout_constraintBottom_toBottomOf="@+id/tv_operator_times"
        app:layout_constraintEnd_toEndOf="@+id/tv_dot"
        app:layout_constraintStart_toStartOf="@+id/tv_operator_div"
        app:layout_constraintTop_toTopOf="@+id/tv_operator_times" />

    <TextView
        android:id="@+id/result"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#CCA9F4"
        android:gravity="right|center_vertical"
        android:paddingEnd="16dp"
        android:text="0"
        android:textColor="@android:color/white"
        android:textSize="58sp"
        app:layout_constraintBottom_toTopOf="@+id/flow"
        app:layout_constraintEnd_toEndOf="@+id/flow"
        app:layout_constraintStart_toStartOf="@+id/flow"
        app:layout_constraintTop_toTopOf="parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

布局里面使用的text_style样式:

    <style name="text_style">
        <item name="android:layout_width">90dp</item>
        <item name="android:layout_height">90dp</item>
        <item name="android:background">#aaFF88</item>
        <item name="android:gravity">center</item>
        <item name="android:textColor">#FFFFFF</item>
        <item name="android:textSize">30sp</item>
    </style>

这样,你可以直接拷贝上面的代码进行自己的尝试了。

最后

谢谢🙏有什么意见和建议,欢迎指正!

  • 19
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值