ConstraintLayout-进阶的RelativeLayout

本文深入解析了Android中的ConstraintLayout,介绍了其基本用法、高级特性,包括比例控制、导航线及链条布局等,并对比了与RelativeLayout和LinearLayout的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

近日伴随着Android Studio 2.2的正式推送,最大的亮点是布局的蓝图模式以及与之配套的ConstraintLayout关注度一下子高了起来.

ConstraintLayout,翻译过来,可以叫约束布局,其子view就是通过一个个属性的约束,来决定自己的位置,大小,而传统的RelativeLayout也类似,所以可以看成是RelativeLayout的一种进化版版本,属性布局用法相对RelativeLayout来说较为复杂,但是当你熟悉之后你会爱上它的.

众所周知,Android APP的布局复杂度会极大的影响程序的流畅度,传统的ViewGroup用的最多的就是RelativeLayout与LineaLayout.

一般能用RelativeLayout替换LineaLayout就替换,因为LinearLayout虽然简单,但是会加深层级.

而有时候却不得不使用LinearLayout,在于LinearLayout有一个layout_weight属性,可以设置LinearLayout的ChildView按照一定的比例布局,这是RelativeLayout做不到的.
ConstraintLayout的其他的属性和用法基本与RelativeLayout一致,如果对RelativeLayout比较熟悉的童鞋很容易上手,而ConstraintLayout最大的优点便是可以添加比例的控制.

准备工作

  • 下载Android Studio2.2
  • 新建一个项目,打开MainActivity布局,并切换到Design(左下角)设计视图
  • 选中layout根目录,右键,Convert转换,将layout转换为ConstraintLayout为根目录的layout

Paste_Image.png
  • 初次转换会提示你没有 ConstraintLayout,问你是否下载,直接选择下载即可,等下载好了会自动转换
  • 也可以直接在Module的gralde配置里添加

      dependencies {
         compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
         }

蓝图介绍


Paste_Image.png
  1. 普通视图
  2. 蓝图
  3. 普通视图和蓝图同在
  4. 关闭/打开布局的约束
  5. 关闭/打开自动为View添加约束
  6. 添加导航线
  7. 查看布局的警告/错误

基本约束属性

前面说了, ConstraintLayout是RelativeLayout的进化版,如果RelativeLayout的子view没有设置任何基本属性,则置于左上角,ConstraintLayout同理.

ConstraintLayout基本属性的值可以是某个控件的id,也可以是"parent",简单的概括就是top,bottom,left,right,baseline.

以bottm为例


Paste_Image.png
约束属性值为id时对照表
约束属性RelativeLayout属性
layout_constraintBaseline_toBaselineOflayout_alignBaseline
layout_constraintBottom_toBottomOflayout_alignBottom
layout_constraintBottom_toTopOflayout_above
layout_constraintEnd_toEndOflayout_alignEnd
layout_constraintEnd_toStartOflayout_toStartOf
layout_constraintLeft_toLeftOflayout_alignLeft
layout_constraintLeft_toRightOflayout_toRightOf
layout_constraintRight_toRightOflayout_alignRight
layout_constraintRight_toLeftOflayout_toLeftOf
layout_constraintStart_toStartOflayout_alignStart
layout_constraintStart_toEndOflayout_toStartOf
layout_constraintTop_toTopOflayout_alignTop
layout_constraintTop_toBottomOflayout_below
约束属性值等于parent

当基本属性值为parent时,必须成对出现才有意义,即top与bottom,left与right,start与end成对.
在使用RelativeLayout的时候,假设子View设置了属性,则子View会置于底部

layout_alignParentBottom="true"

而对约束布局的子Viuew设置,是没有任何效果的,因为parent属性必须成对出现

layout_constraintBottom_toBottomOf="parent"

如果设置成对属性,会发现控件在设置的方向上居中了,也达不到置于底部的效果


Paste_Image.png

这时候前面介绍的功能派上用场了

ConstraintLayout最大的优点便是可以添加比例的控制

比例属性闪亮登场

layout_constraintHorizontal_bias="0.4"
layout_constraintVertical_bias="0.6"

这两个属性接受浮点型,是一个比例,数值在0-1之间,如不写这属性,默认为0.5

点击左下角切换的Design,再点击Button选中这个控件,右边栏会出现约束属性图


Paste_Image.png


简单介绍一下这个属性图

  1. 箭头向里 表示控件的宽度/高度是适应内容的,弹簧状 表示控件是宽度/高度是具体数值
  2. 上下两个0表示控件的上下margin是0
  3. 小球50这个数值表示在垂直方向上,上下的比例是0.5:0.5

鼠标拖动小球,上下移动会发现数值,控件垂直方向上的位置都跟着改变.拖动到20,切换到代码,会发现代码新增了一个属性,此时控件的上下比例是:0.8:0.2

layout_constraintVertical_bias="0.8"

Paste_Image.png

假设要达到layout_alignParentBottom="true"的效果,只需要加上top,bottom约束,并设置layout_constraintVertical_bias="1.0"即可


Paste_Image.png

控件大小比例属性情况

只有一个方向约束:

可以以比例去定义View的宽高。 
为了做到这一点,需要将至少一个约束维度设置为0dp(即MATCH_CONSTRAINT) 
并将属性layout_constraintDimentionRatio设置为给定的比例。

例如:

    <Button
        android:layout_width="200dp"
        android:layout_height="0dp"
        android:text="Ratio"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="2:1"
        app:layout_constraintTop_toTopOf="parent"/>

  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如图:

比例值有两种取值: 
* 浮点值,表示宽度和高度之间的比率 (2,0.5) 
* “width:height”形式的比例 (5:1,1:5)

当约束多于一个(宽高都被约束了)

如果两个维度均设置为MATCH_CONSTRAINT(0dp),也可以使用比例。 在这种情况下,系统会使用满足所有约束条件和比率的最大尺寸。 
如果需要根据一个维度的尺寸去约束另一个维度的尺寸。 
则可以在比率值的前面添加 W 或者 H 来分别约束宽度或者高度

例如,如果一个尺寸被两个目标约束(比如宽度为0,在父容器中居中),可以使用 W 或H 来指定哪个维度被约束。

    <Button
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="H,2:1"
        app:layout_constraintTop_toTopOf="parent"/>
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里用“H”这句话表示已宽度为准,维持2:1的比例
若原H>W则上面效果的宽高比为:1:2;若原H<W,则上面效果的宽高比为2:1;

creator迷之属性

app:layout_constraintBaseline_creator="12"
app:layout_constraintTop_creator="12"
app:layout_constraintBottom_creator="12"
app:layout_constraintLeft_creator="12"
app:layout_constraintRight_creator="12"

creator接受整型属性值,但该属性在 1.0.0-alpha8 版本暂时未有任何作用,查看源码发现,ConstraintLayout只是对改属性值进行了接受,但是没有做任何处理,相信在后续版本会新增其功能

else if(attr != styleable.ConstraintLayout_Layout_layout_constraintLeft_creator && attr != styleable.ConstraintLayout_Layout_layout_constraintTop_creator && attr != styleable.ConstraintLayout_Layout_layout_constraintRight_creator && attr != styleable.ConstraintLayout_Layout_layout_constraintBottom_creator && attr != styleable.ConstraintLayout_Layout_layout_constraintBaseline_creator) {
                    Log.w("ConstraintLayout", "Unknown attribute 0x" + Integer.toHexString(attr));
                }

Guideline导航线

app:layout_constraintGuide_begin="50dp"
app:layout_constraintGuide_end="50dp"
app:layout_constraintGuide_percent="50"

介绍导航线之前,先想一下,根据上面对ConstraintLayout的介绍,要你布置一个菜单控件,菜单栏每一项均分屏幕宽度,按照以前使用LinearLayout,会把每一个子view的weight设置为1,则均分了屏幕宽度

<LinearLayout>
    <View/>
    <View/>
    <View/>
</LinearLayout>

而使用ConstraintLayout则会发现前面介绍的比例,是相对parent来说的,如果要均分屏幕宽度,必须借助透明的分割线来布局

<ConstraintLayout>
    <View,右边约束分割线1/>
    <分割线1,距离左边33%/>
    <View,左边约束分割线1,右边约束分割线2/>
    <分割线2,距离左边66%/>
    <View,左边约束分割线2/>
</ConstraintLayout>

而这个分割线其实谷歌已经帮我们写好了,就是Guideline.蓝图介绍中,6对应的就是添加导航线.切换到蓝图模式,点击6,就可以添加一个水平/垂直的导航线


Paste_Image.png

添加垂直导航线


Paste_Image.png
注意:图中的导航线有一个向左的箭头模式,除了这个模式还有向右,百分比模式.如果导航线是水平的,还会有上下箭头.
点击小球即可切换模式

Paste_Image.png

Guideline属性对照表

属性箭头
layout_constraintGuide_begin左/上
layout_constraintGuide_end右/下
layout_constraintGuide_percent百分比

Guideline属性值

Guideline本身对于用户来说是不可见的,所以其宽高的值没有任何意义,也不起作用.

<android.support.constraint.Guideline
    android:id="@+id/guideline"
    android:layout_width="wrap_content"//无意义
    android:layout_height="511dp"//无意义
    android:orientation="vertical"//决定这是一条水平导航线还是垂直导航线
    app:layout_constraintGuide_percent="0.333"//决定导航线的位置
/>

此时再添加一条比例为0.666的导航线,即可三等分屏幕(点击图片可查看详细代码)


Paste_Image.png

链条(Chains)

链条在同一个轴上(水平或者垂直)提供一个类似群组的统一表现。另一个轴可以单独控制。

创建链条(Creating a chain)

如果一组小部件通过双向连接(见图,显示最小的链,带有两个小部件),则将其视为链条。

链条头(Chain heads)

链条由在链的第一个元素(链的“头”)上设置的属性控制: 

头是水平链最左边的View,或垂直链最顶端的View。

链的margin(Margins in chains)

如果在连接上指定了边距,则将被考虑在内。 
例如

    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:text="Button"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/buttonB"/>

    <Button
        android:id="@+id/buttonB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintLeft_toRightOf="@+id/buttonA"
        app:layout_constraintRight_toRightOf="parent"/>
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

通过app:layout_constraintRight_toLeftOf="@+id/buttonB"app:layout_constraintLeft_toRightOf="@+id/buttonA"就建立了链条,(我中有你,你中有我)。 
然后它们两个成了一个整体,所以链条左边设置app:layout_constraintLeft_toLeftOf="parent" 使得和父控件左对齐, 
右边设置app:layout_constraintRight_toRightOf="parent"使得和父控件右对齐, 
这样整个链条就居中了,最后对左控件设置了margin,相当于整个链条左边有了margin 
效果:

链条样式(Chain Style)

当在链的第一个元素上设置属性 layout_constraintHorizontal_chainStylelayout_constraintVertical_chainStyle 时,链的行为将根据指定的样式(默认为CHAIN_SPREAD)而更改。 
看图这里就很像js里的flexible有木有。因为ConstraintLayout就是模仿flexible做的。


取值如下: 
spread - 元素将被展开(默认样式) 
* 加权链 - 在spread模式下,如果某些小部件设置为MATCH_CONSTRAINT,则它们将拆分可用空间 
spread_inside - 类似,但链的端点将不会扩展 
packed - 链的元素将被打包在一起。 孩子的水平或垂直偏差属性将影响包装元素的定位

拿加权链举个例子:

    <Button
        android:id="@+id/buttonA"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:text="Button"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/buttonB"/>

    <Button
        android:id="@+id/buttonB"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintLeft_toRightOf="@+id/buttonA"
        app:layout_constraintRight_toRightOf="parent"/>
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

加权链(Weighted chains)

LinearLayout的weight类似。

链的默认行为是在可用空间中平均分配元素。 如果一个或多个元素使用MATCH_CONSTRAINT,它们将使用剩余的空白空间(在它们之间相等)。 属性layout_constraintHorizontal_weightlayout_constraintVertical_weight将决定这些都设置了MATCH_CONSTRAINT的View如何分配空间。 例如,在包含使用MATCH_CONSTRAINT的两个元素的链上,第一个元素使用权重为2,第二个元素的权重为1,第一个元素占用的空间将是第二个元素的两倍

最后关于链条,再给大家看一个关于margin的demo:

    <Button
        android:id="@+id/buttonA"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="50dp"
        android:text="Button"
        app:layout_constraintHorizontal_chainStyle="spread"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/buttonB"/>

    <Button
        android:id="@+id/buttonB"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintLeft_toRightOf="@+id/buttonA"
        app:layout_constraintRight_toRightOf="parent"/>
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

 
一图胜千言,可以看到虽然他们的weight相等,但是margin是被计算在约束里的,所以左边的按钮宽度比右边的小。


总结

ConstraintLayout完美的结合了RelativeLayout与LinearLayout的特点,减少了布局的层级,展现了其强大的功能.

除了上述介绍到的功能之外,ConstraintLayout的子view被设置为GONE后,依赖这个view的约束会自动继承这个子view的约束,从而保证布局不会错乱.而且还可以单独设置控件隐藏/显示时的外边距.

蓝图模式让view与view之间的依赖关系更加的清晰明了,还可以快速设置属性值.

有人认为拖动控件必将成为主流,而博主实际体验,当控件非常复杂的时候,非常多的约束也会让人眼花缭乱.

其实最好的方式还是用蓝图与代码结合的方式,在创建,快速设置依赖关系,以及设置属性的时候,使用蓝图模式,在细微的调整的时候,使用代码模式.

再实际使用过程中发现:

tools:layout_editor_absoluteY="0dp"
这类代码有时会自动生成,官方解释如下
        This view is not constrained vertically: at runtime it will jump to the     left unless you add a vertical constraint less...     (Ctrl+F1)        

The layout editor allows you to place widgets anywhere on the canvas, and it records the current position with designtime attributes (such as layout_editor_absoluteX.) These attributes are not applied at runtime, so if you push your layout on a device, the widgets may appear in a different location than shown in the editor. To fix this, make sure a widget has both horizontal and vertical constraints by dragging from the edge connections.     

看来官方原意思是想大家再创建view的时候把4个方向的约束都建立好,但这是建立在可视化拖动的基础上,事实我们在用的时候总是用代码去写约束,(These attributes are not applied at runtime)虽然这些代码不会影响实际的运行效果,但是提现在代码里总会产生误导,我是在ConstraintLayout内添加relativelayout时自动生成这些代码的,所以我的建议是:大家既然接受了ConstraintLayout,那就热情的去拥抱它,放弃以前的relativelayout跟LinearLayout吧,就像我们当初放弃eclipse一样可怜




原文链接:http://www.jianshu.com/p/d64d845b6b90
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值