约束布局解析

##1、相对定位:
属性都形如 layout_constraint’DIRECTION’_to’TARGET DIRECTION’Of=”TARGET“

1、constraint’DIRECTION’ 里的 'DIRECTION’代表是这个子控件自身的哪条边
2、to’TARGET DIRECTION’Of 里的 ‘TARGET DIRECTION’ 代表的是和约束控件的哪条边发生约束
3、TARGET 为目标约束控件对应的 id(父控件的 id 理解为 parent)

##2、Margin
###1]注意点:

Note that a margin can only be positive or equals to zero, and takes a Dimension
1、只能为正值
2、必须指定维度

####1)、只能为正值
当值设置为非正时,会使用默认值 0

margin 值对应的变量都在 LayoutParams 中

源码:

  • ConstraintLayout#setChildrenConstraints 方法中设置 margin 相关代码
  • 直接代码为:

####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"
              android:layout_width="match_parent"
              android:layout_height="match_parent"

>

    <Button
            android:id="@+id/fixed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="fixed"
            android:layout_marginLeft="50dp"
            android:layout_marginTop="50dp"
    />
</android.support.constraint.ConstraintLayout>

添加横向维度的“相对“约束时
app:layout_constraintLeft_toLeftOf="parent"

再添加纵向维度的”相对“约束时
app:layout_constraintTop_toTopOf="parent"

####2] 要连接的控件 GONE 时 的 margin

layout_goneMargin’DIRECTION’

<?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"
                                             android:layout_width="match_parent"
                                             android:layout_height="match_parent"

>

    <Button
            android:id="@+id/fixed"
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:text="fixed"
            android:visibility="visible"
    />
    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="floating"
            app:layout_constraintLeft_toRightOf="@+id/fixed"
            app:layout_goneMarginLeft="120dp"

            android:layout_marginTop="50dp"
            app:layout_constraintTop_toTopOf="parent"
    />
</android.support.constraint.ConstraintLayout>


当 fixed button 设置为 gone 时

tips:在布局编辑器中,将 fixed 的 visibility 设置为 gone 时更容易看出效果
即使 target gone 了,但是其目标对象仍然没变,只不过目标由一个矩形变成一个点了

注意:这个点是 fixed 控件(也就是设置 layout_goneMarginXXX 属性的参照物) “缩小”形成的

这个点的(参照对 MATCH_CONSTRAINT 属性的解释)

  • x 坐标是 fixed 控件满足横向约束范围的中间点;(如果 left 未设置参照,那么以 right 参照点为 x 坐标;如果 right 未设置参照,那么以 left 参照点为 x 坐标)
  • y 坐标是 fixed 控件满足纵向约束范围的中间点(与 x 坐标同理)

下面这个例子是 left、right、top、bottom 都设置参照点的情况

上图中对应的 xml 为:

<?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"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
>
    <Button
            android:id="@+id/btnFixed"
            android:layout_width="30dp"
            android:layout_height="wrap_content"
            android:text="Fixed"

            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/btnFixedOther"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            
    />

    <Button
            android:id="@+id/btnFixedOther"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintRight_toRightOf="parent"
            android:text="FixedOther"
    />

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@+id/btnFixed"

            app:layout_goneMarginLeft="100dp"
            app:layout_constraintBottom_toBottomOf="parent"
            android:text="floating"
    />

</android.support.constraint.ConstraintLayout>

下面这个例子是 left、right、top 设置了参照点,而 bottom 未设置参照点的情况

上图中对应的 xml 为:

<?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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
>
    <Button
            android:id="@+id/btnFixed"
            android:layout_width="30dp"
            android:layout_height="wrap_content"
            android:text="Fixed"

            android:visibility="gone"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/btnFixedOther"
            app:layout_constraintTop_toTopOf="parent"

    />

    <Button
            android:id="@+id/btnFixedOther"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintRight_toRightOf="parent"
            android:text="FixedOther"
    />

    <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toRightOf="@+id/btnFixed"

            app:layout_goneMarginLeft="100dp"
            app:layout_constraintBottom_toBottomOf="parent"
            android:text="floating"
    />

</android.support.constraint.ConstraintLayout>

##3、居中定位和倾向
###1)居中
控件的左边和父控件的左边对齐,控件的右边和父控件的右边对齐

<Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:text="button3"
            
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
    />
</android.support.constraint.ConstraintLayout>


对比

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:text="button3"

            android:layout_alignParentBottom="true"
            android:layout_alignParentLeft="true"
            android:layout_alignParentRight="true"
    />
</RelativeLayout>


###2)倾向(bias)

倾向的值的分配上可以类比 weight

  • layout_constraintHorizontal_bias
    (0最左边 1最右边)
  • layout_constraintVertical_bias
    (0最上边 1 最底边)
    例子:
 <Button
            android:layout_width="wrap_content"
            android:layout_height="200dp"
            android:text="floating"


            app:layout_constraintHorizontal_bias="0.6"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"

            app:layout_constraintVertical_bias="0.7"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
    />

注意:图中红色箭头只是辅助理解作用

横向:(目标值:0.6)
200 / (200 + 134) = 0.5988
248 / (248 + 183) = 0.5754
竖向:
1、

目标值:0.7
291 / (291 + 126) = 0.6978
402 / (402 + 238) = 0.6281

2、

目标值:0.8
332 / (332 + 83) = 0.8
444 / (444 + 197) = 0.69

##4、尺寸约束
###1)最小尺寸

对应属性为 wrap_content 时起作用

  • android:minWidth
  • android:minHeight
    ###2)控件尺寸约束
  • 确切的值
  • wrap_content
  • 0dp( match_constraint )

注意没有 match_parent
可以这么理解,也可以自己试一下,当某个方向上设置为 match_parent 时,对应方向上的所有约束都不再生效了,但是“约束布局”本身最重要的是约束

##MATCH_CONSTRAINT
在对应的约束方向上充满“约束的最大范围”

约束的最大范围:

比如:(此处只说横向)

 <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            app:layout_constraintLeft_toRightOf="@+id/button1"
            app:layout_constraintRight_toLeftOf="@+id/button4"

            android:layout_height="wrap_content"
            android:text="button2"

            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="80dp"
    />

预计:

约束的最大范围时两个红色箭头之间的距离(也就是两个虚线之间的范围)


实际:

再比如:

<Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            app:layout_constraintLeft_toRightOf="@+id/button1"
            app:layout_constraintRight_toRightOf="parent"

            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@id/button2"
            android:text="button3"
    />

预计:

约束的最大范围是两个红色箭头之间的距离(也就是两个虚线之间的范围)


实际:

##5、比例

tips:至少一个约束维度设置为 0dp(MATCH_CONSTRAINT),并将 layout_constraintDimentionRatio 设定为给定的比例

比例可以为一个浮点数,或者 width:height 的形式

如果两个约束维度都不是 0dp ,那么比例设置无效
如果两个约束维度都是 0dp 时,系统会使用满足所有约束条件和比率的最大尺寸

##6、链条

几个组件之间通过双向连接链接到一起时,可以认为这几个组件形成了链条

###样式:
####1、spread 分散开的

  • 分散开的:(layout_constraintXXX_chainStyle 默认是此模式)
    分散时包含组件外部
  • 内部分散开的
    分散时只包含组件内部
  • (铺满)加权分散开的
    有组件链条对应维度的大小设置为 0dp( MATCH_CONSTRAINT ) , 此时整个链条会铺满对应维度;
    比如横向上有两个组件组成链条时:

####2、packed 挤在一起的

tips:此模式下如果将组件的链条对应维度大小设置为 0dp( MATCH_CONSTRAINT ),那么设置0dp 的会不显示(注意与 spread 模式区分)

  • 挤在一起的
    默认居中
  • 有偏向性的挤在一起的
    只能由 head 组件指定倾向( bias )

    ##7、Guideline
    辅助 ConstraintLayout 的工具类
  • vertical
    纵向;宽度为零,高度为 ConstraintLayout 的高度
  • horizontal
    横向;高度为零,宽度为 ConstraintLayout 的高度

定位Guideline有三种方式:

  • 指定距离左侧或顶部的固定距离(layout_constraintGuide_begin)
  • 指定距离右侧或底部的固定距离(layout_constraintGuide_end)
  • 指定在父控件中的宽度或高度的百分比(layout_constraintGuide_percent)

###设置固定距离


    <android.support.constraint.Guideline
            android:id="@+id/guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="100dp"
    />

    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="Button"
            app:layout_constraintLeft_toLeftOf="@+id/guideline"
            app:layout_constraintTop_toTopOf="parent"/>

###设置百分比

<android.support.constraint.Guideline
            android:id="@+id/guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.6"
    />

    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="Button"
            app:layout_constraintLeft_toLeftOf="@+id/guideline"
            app:layout_constraintTop_toTopOf="parent"/>

###同时设置固定距离和百分比

<android.support.constraint.Guideline
            android:id="@+id/guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.6"
            app:layout_constraintGuide_begin="100dp"
    />

    <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="Button"
            app:layout_constraintLeft_toLeftOf="@+id/guideline"
            app:layout_constraintTop_toTopOf="parent"/>

虽然显示 100,但是实际距离还是按照百分比算的,以下为原因


android.support.constraint.solver.widgets.Guideline

public void setGuidePercent(float value) {
        if(value > -1.0F) {
            this.mRelativePercent = value;
            this.mRelativeBegin = -1;
            this.mRelativeEnd = -1;
        }
    }

    public void setGuideBegin(int value) {
        if(value > -1) {
            this.mRelativePercent = -1.0F;
            this.mRelativeBegin = value;
            this.mRelativeEnd = -1;
        }
    }

    public void setGuideEnd(int value) {
        if(value > -1) {
            this.mRelativePercent = -1.0F;
            this.mRelativeBegin = -1;
            this.mRelativeEnd = value;
        }
    }

============================
动态

tips:一定注意最后要调用 ConstraintSet#applyTo 方法
#1、ConstraintsSet 创建方式

  • Manually
    c = new ConstraintSet(); c.connect(....);
  • from a R.layout.* object
    c.clone(context, R.layout.layout1);
  • from a ConstraintLayout
    c.clone(clayout);

#2、各属性对应代码:

##1)相对定位

void connect (int startID, int startSide, int endID, int endSide, int margin)
		Create a constraint between two widgets.

例如:xml

app:layout_constraintLeft_toLeftOf="parent"

对应代码:

constraintSet1.connect(R.id.tvTurn, ConstraintSet.LEFT, ConstraintSet.PARENT_ID, ConstraintSet.LEFT);

tips:如果设置某维度非居中的对齐方式,尽量把维度对应的另一个方向也设置上,设置为 UNSET
原因:之前控件为居中的,现在改为右对齐时,如果只设置一个方向,那么不会生效

如右对齐时:

constraintSetDemo.connect(R.id.demoView, ConstraintSet.LEFT,  ConstraintSet.UNSET, ConstraintSet.LEFT);
                constraintSetDemo.connect(R.id.demoView, ConstraintSet.RIGHT, ConstraintSet.PARENT_ID, ConstraintSet.RIGHT);

#2、margin
基本规则与 xml 中一致

设置 margin 时遇到的问题:

表象:

涉及到左右 margin 的都无效

原因:

暂时不知道
Stack Overflow 上也有个类似问题,不过没人说是怎么回事,猜测是 bug
https://stackoverflow.com/questions/44129278/adding-constraints-to-a-view-in-a-constraintlayout-ignore-left-and-right-margins

解决方式:

把 left 改成 start,right 改成 end,目前基本都是从左往右的;
比如

constraintSet3.setMargin(R.id.iconCompass, ConstraintSet.START, (int) ViewHelper.dpToPx(100, getApplicationContext()));
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值