xml布局详细介绍

  • xml布局详细介绍
  • 1、线性布局 LinearLayout
  • 1.1、水平布局(horizontal)
  • 1.2、垂直布局(vertical)
  • 1.3、layout_weight属性示例
  • 1.4、常见问题
  • 2、相对布局 RelativeLayout
  • 2.1、定义
  • 2.2、属性说明
  • 2.3、padding与layout_margin的区别与用法
  • 3、相对布局 ConstraintLayout
  • 3.1、app:layout_constraint基础属性详解
  • 3.2、约束布局的margin控制
  • 3.3、关于view gone
  • 3.4、居中及bias
  • 3.5、view的尺寸
  • 3.6、Guideline和Barrier
  • 参考


xml布局详细介绍

Android推荐使用XML布局文件来定义用户界面,而不是使用Java代码来开发用户界面,因此基础所有组件都提供了两种方式来控制组件的行为:1、在XML布局文件中通过XML属性进行控制;2、在Java程序代码中通过调用方法进行控制;完全使用Java代码来控制UI界面不仅繁琐、而且不利于解耦;而完全利用XML布局文件来控制UI界面虽然方便、便捷,但难免有失灵活。

在编译XML布局文件时,都需要通过main函数中的 onCreate 来调用,通常使用如下方式来进行调用。

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_linear_layout);
    }

其中setContentView里的内容‘R’为就是系统给声明的静态变量,所有的子类都是通过‘R’来调用。而‘R’的路径则是 Android 工程路径下的 app -> src -> main -> res 下。

R.layoutres文件夹下一个名为layout的文件夹,而R.layout.activity_linear_layout之后的部分则是layout文件夹下自己定义的.xml文件。

常用的布局方式,

线性布局:LinearLayout

 条件布局:ConstraintLayout

 相对布局:RelativeLayout

1、线性布局 LinearLayout

Android xml布局无法预览 android xml布局原理_LinearLayout


Android xml布局无法预览 android xml布局原理_Android xml布局无法预览_02

一、线性布局LinearLayout最常用的属性有:

  • android:id :定义布局id,即标识符,可以通过id来找到该布局或者控件
  • android :layout_width :布局宽度,有match_parent ,wrap_content,fill_paren
  • android:layout_height :布局高度,有match_parent,wrap_content,fill_paren
  • android:background :设置布局的背景,可以用颜色,也可以使用图片,颜色常以六位的十六进制表示
  • android:layout_margin :外边距,布局或控件距离外部元素的边距
  • android:layout_padding :内边距,布局或控件距离内部元素的边距
  • android:orientation :布局方向,水平布局horizontal,垂直布局vertical
  • 当 android:orientation=“vertical” 时, 只有水平方向的设置才起作用,垂直方向的设置不起作用。即:left(左),right(右),center_horizontal(水平居中) 是生效的。
  • 当 android:orientation=“horizontal” 时,只有垂直方向的设置才起作用,水平方向的设置不起作用。即:top(上),bottom(下),center_vertical(垂直居中)是生效的。
  • android:layout_weight:权重,除了被显示占据的空间以外的的空间,然后根据权重的大小来分配空间,使用权重通常会把分配该权重方向的宽度设置为0dp,如果未设置0dp,则该控件会占据指定的宽度,然后再加上根据权重来分配的空间

1.1、水平布局(horizontal)

代码如下:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第一个文本框"
            android:background="#ffff00"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第二个文本框"
            android:background="#ff0000"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第三个文本框"
            android:background="#008000"/>
    </LinearLayout>

Android xml布局无法预览 android xml布局原理_RelativeLayout_03

1.2、垂直布局(vertical)

<LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:orientation="vertical">
       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:gravity="center"
           android:text="第一个文本框"
           android:background="#ffff00"/>
       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:gravity="center"
           android:text="第二个文本框"
           android:background="#ff0000"/>
       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:gravity="center"
           android:text="第三个文本框"
           android:background="#008000"/>
   </LinearLayout>

Android xml布局无法预览 android xml布局原理_Android xml布局无法预览_04

1.3、layout_weight属性示例

在这之前先简要说明下match_parentfill_parentwrap_content

  • match_parent和fill_parent在效果上相同,都是使该组件的宽度或高度填满父元素,也就是说令其宽度或高度与父元素的宽度或高度一致。
  • 用 wrap_content产生的效果由该组件本身的实际情况决定,例如一个TextView组件,它的宽度就是由文本实际的内容多少决定的。

回到正题,weight,这里理解为权重,或是比例,需要注意的是:

  • 水平布局时,按比例分配父元素的宽度(width)
  • 竖直布局时,按比例分配父元素的高度(height)
  • android:layout_weight="1"通过设置控件的layout_weight属性以控制各个控件在布局中的相对大小,线性布局会根据该控件layout_weight值与其所处布局中所有控件layout_weight值之和的比值为该控件分配占用的区域。
  • 在水平布局的LinearLayout中有4个TxtView,这4个TextView的layout_weight属性值都为1,那么这4个TextView的大小将拉伸到总大小的四分之一。
  • 如果layout_weight指为0,控件会按原大小显示,不会被拉伸;对于其余layout_weight属性值大于0的控件,系统将会减去layout_weight属性值为0的控件的宽度或者高度,再用剩余的宽度或高度按相应的比例来分配每一个控件显示的宽度或高度。
  • 权重最基本的用法就是 对线性布局指定方向(水平或垂直)上剩余空间分配的一个规则,先把规定的大小占完,再来按比例分配剩余空间

先看一个例子(以水平布局举例):

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第一个文本框"
            android:layout_weight="1"
            android:background="#ffff00"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第二个文本框"
            android:layout_weight="1"
            android:background="#ff0000"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第三个文本框"
            android:layout_weight="1"
            android:background="#008000"/>
    </LinearLayout>

还是之前水平布局的例子,只是我们给每个TextView添加了一句android:layout_weight=“1”

Android xml布局无法预览 android xml布局原理_LinearLayout_05


貌似达到了我们的预期,三个TextView按1:1:1分完了总宽度。

但有时候可能无法达到预期的效果,请看下面的例子,将三个TextView的android:layout_weight依次设置为'1','2','3'

Android xml布局无法预览 android xml布局原理_条件布局_06


可以看出三个文本框的大小不一致了,但是明显没有达到我们预期的1:2:3的效果。

这是因为我们设置的每个TextView的宽度为wrap_content每个组件都有本身由text内容决定的宽度,只是将剩余的在宽度上的空间按比例分好后再加在其本身的宽度上。

所以说,为达到真正的比例效果:

水平布局时,将每个组件的宽度(width)设置为0dp
竖直布局时,将每个组件的高度(height)设置为0dp
<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第一个文本框"
            android:layout_weight="1"
            android:background="#ffff00"/>
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第二个文本框"
            android:layout_weight="2"
            android:background="#ff0000"/>
        <TextView
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="第三个文本框"
            android:layout_weight="3"
            android:background="#008000"/>
    </LinearLayout>

Android xml布局无法预览 android xml布局原理_android_07

1.4、常见问题

(1)文字没有对齐如何解决?

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    
    <TextView 
        android:layout_width="0dp"
        android:layout_height="45dp"
        android:layout_weight="1"
        android:background="#55ff0000"
        android:gravity="center"
        android:text="aaaaaaaaaaaaaaaa"/>
    
    
    <TextView 
        android:layout_width="0dp"
        android:layout_height="45dp"
        android:layout_weight="1"
        android:background="#5500ff00"
        android:gravity="center"
        android:text="b"/>
    
    <TextView 
        android:layout_width="0dp"
        android:layout_height="45dp"
        android:layout_weight="1"
        android:background="#550000ff"
        android:gravity="center"
        android:text="c"/>
 
</LinearLayout>

Android xml布局无法预览 android xml布局原理_Android xml布局无法预览_08


很明显,第一个textview的对齐方式不是我们想要的,出现这种现象的原因是LinearLayout默认子控件按照基准线对齐,所以我们需要在LinearLayout`中添加一个属性就即可解决:android:baselineAligned=“false” 使得子控件不按照基线对齐。

(3)weightSum属性得使用

如果布局中只有一个控件,并且我只想让它占据1/2的宽度,这个时候就可以利用LinearLayout的weightSum属性,来看看这个例子:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:weightSum="2">
    
    <TextView 
        android:layout_width="0dp"
        android:layout_height="45dp"
        android:layout_weight="1"
        android:background="#55ff0000"
        android:gravity="center"
        android:text="aaaaaaaaaaaaaaaa"/>
    
 
</LinearLayout>

Android xml布局无法预览 android xml布局原理_RelativeLayout_09

2、相对布局 RelativeLayout

2.1、定义

相对布局:相对布局也是常用的布局之一,它可以设置某一个控件相对于其他控件的位置,这些位置可以包括上下左右等,因此相较于其他的布局方式而言具有很大的灵活性。

1.核心属性图

Android xml布局无法预览 android xml布局原理_android_10


2.父容器定位属性示意图

Android xml布局无法预览 android xml布局原理_Android xml布局无法预览_11


3.根据兄弟组件定位恩,先说下什么是兄弟组件吧,所谓的兄弟组件就是处于同一层次容器的组件,如图

Android xml布局无法预览 android xml布局原理_android_12

  • 图中的组件1,2就是兄弟组件了,而组件3与组件1或组件2并不是兄弟组件,所以组件3不能通过 组件1或2来进行定位,比如layout_toleftof = "组件1"这样是会报错的!

切记! 关于这个兄弟组件定位的最经典例子就是"梅花布局"了,下面代码实现下:

Android xml布局无法预览 android xml布局原理_RelativeLayout_13

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    xmlns:tools="http://schemas.android.com/tools"    
    android:id="@+id/RelativeLayout1"    
    android:layout_width="match_parent"    
    android:layout_height="match_parent" >    
    
    <!-- 这个是在容器中央的 -->    
        
    <ImageView    
        android:id="@+id/img1"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_centerInParent="true"    
        android:src="@drawable/pic1"/>    
        
    <!-- 在中间图片的左边 -->    
    <ImageView    
        android:id="@+id/img2"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_toLeftOf="@id/img1"    
        android:layout_centerVertical="true"    
        android:src="@drawable/pic2"/>    
        
    <!-- 在中间图片的右边 -->    
    <ImageView    
        android:id="@+id/img3"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_toRightOf="@id/img1"    
        android:layout_centerVertical="true"    
        android:src="@drawable/pic3"/>    
        
    <!-- 在中间图片的上面-->    
    <ImageView    
        android:id="@+id/img4"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_above="@id/img1"    
        android:layout_centerHorizontal="true"    
        android:src="@drawable/pic4"/>    
        
    <!-- 在中间图片的下面 -->    
    <ImageView    
        android:id="@+id/img5"     
        android:layout_width="80dp"    
        android:layout_height="80dp"    
        android:layout_below="@id/img1"    
        android:layout_centerHorizontal="true"    
        android:src="@drawable/pic5"/>    
    
</RelativeLayout>

2.2、属性说明

相对布局常用的属性:

第一类:属性值为true或false:相对于父元素的位置

android:layout_centerHrizontal 水平居中
  android:layout_centerVertical 垂直居中
  android:layout_centerInparent 相对于父元素完全居中
  android:layout_alignParentBottom 贴紧父元素的下边缘
  android:layout_alignParentLeft 贴紧父元素的左边缘
  android:layout_alignParentRight 贴紧父元素的右边缘
  android:layout_alignParentTop 贴紧父元素的上边缘
  android:layout_alignWithParentIfMissing 如果对应的兄弟元素找不到的话就以父元素做参照物

实例:

<?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"
    android:background="@android:color/white">
  <TextView
      android:id="@+id/middle"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerInParent="true"
      android:background="@android:color/holo_blue_bright"
      android:text="我在最中间"/>
</RelativeLayout>

Android xml布局无法预览 android xml布局原理_条件布局_14

第二类:属性值必须为id的引用名“@id/id-name”,

//相对于子元素外部的位置
android:layout_below 在某元素的下方
  android:layout_above 在某元素的的上方
  android:layout_toLeftOf 在某元素的左边
  android:layout_toRightOf 在某元素的右边
//相对于子元素内部的位置
  android:layout_alignTop 本元素的上边缘和某元素的的上边缘对齐
  android:layout_alignLeft 本元素的左边缘和某元素的的左边缘对齐
  android:layout_alignBottom 本元素的下边缘和某元素的的下边缘对齐
  android:layout_alignRight 本元素的右边缘和某元素的的右边缘对齐

实例:相对于元素外部四周

<?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"
    android:background="@android:color/white">
  <TextView
      android:id="@+id/middle"
      android:layout_width="200dp"
      android:layout_height="200dp"
      android:gravity="center"
      android:layout_centerInParent="true"
      android:background="@android:color/holo_blue_bright"
      android:text="我在最中间"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/middle"
        android:text="layout_below"
        android:layout_centerHorizontal="true"
        android:background="@android:color/holo_green_light"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/middle"
        android:text="layout_above"
        android:layout_centerHorizontal="true"
        android:background="@android:color/holo_green_light"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@id/middle"
        android:text="layout_toLeftOf"
        android:layout_centerVertical="true"
        android:background="@android:color/holo_green_light"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/middle"
        android:text="layout_toRightOf"
        android:layout_centerVertical="true"
        android:background="@android:color/holo_green_light"/>
</RelativeLayout>

Android xml布局无法预览 android xml布局原理_LinearLayout_15


实例:相对于元素内部四周

<?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"
    android:background="@android:color/white">
  <TextView
      android:id="@+id/middle"
      android:layout_width="200dp"
      android:layout_height="200dp"
      android:gravity="center"
      android:layout_centerInParent="true"
      android:background="@android:color/holo_blue_bright"
      android:text="我在最中间"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/middle"
        android:text="layout_alignBottom"
        android:layout_centerHorizontal="true"
        android:background="@android:color/holo_orange_light"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/middle"
        android:text="layout_alignTop"
        android:layout_centerHorizontal="true"
        android:background="@android:color/holo_orange_light"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@id/middle"
        android:text="alignLeft"
        android:layout_centerVertical="true"
        android:background="@android:color/holo_orange_light"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@id/middle"
        android:text="alignRight"
        android:layout_centerVertical="true"
        android:background="@android:color/holo_orange_light"/>
</RelativeLayout>

Android xml布局无法预览 android xml布局原理_Android xml布局无法预览_16

第三类:属性值为具体的像素值,如30dip,40px

android:layout_marginBottom 离某元素底边缘的距离
  android:layout_marginLeft 离某元素左边缘的距离
  android:layout_marginRight 离某元素右边缘的距离
  android:layout_marginTop 离某元素上边缘的距离

android:gravity

  • android:gravity属性是对该view 内容的限定.比如一个button 上面的text. 你可以设置该text 在view的靠左,靠右等位置.以button为例,android:gravity="right"则button上面的文字靠右

android:layout_gravity

  • android:layout_gravity是用来设置该view相对与起父view 的位置.比如一个button在linearlayout里,你想把该button放在靠左、靠右等位置就可以通过该属性设置.以button为例,android:layout_gravity="right"则button靠右

2.3、padding与layout_margin的区别与用法

Margin:设置组件与父容器(通常是布局)的边距

android:layout_margin: 指定控件的四周的外部留出一定的边距
android:layout_marginLeft: 指定控件的左边的外部留出一定的边距
android:layout_marginTop: 指定控件的上边的外部留出一定的边距
android:layout_marginRight: 指定控件的右边的外部留出一定的边距
android:layout_marginBottom: 指定控件的下边的外部留出一定的边距

Padding:设置组件内部元素间的边距(可以理解为填充)

android:padding :指定控件的四周的内部留出一定的边距
android:paddingLeft: 指定控件的左边的内部留出一定的边距
android:paddingTop: 指定控件的上边的内部留出一定的边距
android:paddingRight: 指定控件的右边的内部留出一定的边距
android:paddingBottom: 指定控件的下边的内部留出一定的边距

android:layout_margin就是设置view的上下左右边框的额外空间

android:padding是设置内容相对view的边框的距离

  • padding,含义为“填充”,像垫肩压类似的填充物,一个控件的padding及此控件内部的填充,由此可见padding是以所被定义的控件A为parent控件,而内部的内容物与控件A的间距。而layout_margin是A控件所在的控件为parent控件,是A与其的间距。
  • 其实概念很简单,padding是站在父view的角度描述问题,它规定它里面的内容必须与这个父view边界的距离。margin则是站在自己的角度描述问题,规定自己和其他(上下左右)的view之间的距离,如果同一级只有一个view,那么它的效果基本上就和padding一样了

Android xml布局无法预览 android xml布局原理_RelativeLayout_17

当按钮分别设置以上两个属性时,得到的效果是不一样的。
android:paddingLeft="30px"
按钮上设置的内容(例如图片)离按钮左边边界30个像素
android:layout_marginLeft="30px"
整个按钮离左边设置的内容30个像素

使用示例

1、两个都不加

<?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"
    android:background="#ffffff" >
 
    <TextView
        android:layout_width="100dip"
        android:layout_height="100dip"
         android:background="#000000"
        android:text="Hello" />
 
</RelativeLayout>

Android xml布局无法预览 android xml布局原理_条件布局_18


2、只加padding

<?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"
     android:padding ="100dip"
    android:background="#ffffff" >
 
    <TextView
        android:layout_width="100dip"
        android:layout_height="100dip"
         android:background="#000000"
        android:text="Hello" />
 
</RelativeLayout>

Android xml布局无法预览 android xml布局原理_RelativeLayout_19


3、只加margin

<?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"
    android:background="#ffffff" >
 
    <TextView
        android:layout_width="100dip"
        android:layout_height="100dip"
         android:background="#000000"
         android:layout_margin="30dip"
        android:text="Hello" />
 
</RelativeLayout>

Android xml布局无法预览 android xml布局原理_android_20


注意说明

  • 在LinearLayout、RelativeLayout、TableLayout中,这2个属性都是设置都是有效的
  • 在FrameLayout中,android:layout_margin是无效的,因为FrameLayout里面的元素都是从左上角开始绘制的
  • 在AbsoluteLayout中,没有android:layout_margin属性

在此提醒,xml参数中含有layout的参数项目为定义的控件相对于parent的关联,没有的一般为本身的定义,以上内容与此相符。又类似于gravity跟layout_gravity,带layout的是相对于parent的大体位置,而不带的是自身内部内容的大体位置。

3、相对布局 ConstraintLayout

在以前,android是使用布局如LinearLayout 、RelativeLayout等来构建页面,但这些布局使用起来很麻烦,并且经常需要一层一层嵌套,写一个简单的页面就需要费很大的劲。

1、较高的性能优势。

  • 布局嵌套层次越高,性能开销越大。而使用ConstraintLayout,经常就一层嵌套就搞定了,所以其性能要好很多。
  • 详细的性能分析可参见:解析ConstraintLayout的性能优势

2、完美的屏幕适配

  • ConstraintLayout的大小、距离都可以使用比例来设置,所以其适配性更好。

3、书写简单

4、可视化编辑。

  • ConstraintLayout也有十分方便完善的可视化编辑器,不用写xml也基本上能实现大部分功能。但个人还是比较喜欢写xml,所以本篇文章主要介绍如何使用代码控制。
api 'com.android.support.constraint:constraint-layout:1.1.2'

3.1、app:layout_constraint基础属性详解

属性

描述

app:layout_constraintLeft_toLeftOf

把A的left side放在B的left side(左边对齐)

app:layout_constraintLeft_toRightOf

把A的left side放在B的right side(左边相对右边对齐)

app:layout_constraintRight_toLeftOf

把A的right side放在B的left side(右边相对左边对齐)

app:layout_constraintRight_toRightOf

把A的right side放在B的right side(右边对齐)

app:layout_constraintTop_toTopOf

把A的top side放在B的top side(顶部对齐)

app:layout_constraintTop_toBottomOf

把A的top side放在B的bottom side(顶部相对底部对齐)

app:layout_constraintBottom_toTopOf

把A的bottom side放在B的top side(底部相对顶部对齐)

app:layout_constraintBottom_toBottomOf

把A的bottom side放在B的bottom side(底部对齐)

app:layout_constraintStart_toEndOf

把A的start position放在B的end position(起始位置相对结束位置对齐)

app:layout_constraintStart_toStartOf

把A的start position放在B的start position(起始位置对齐)

app:layout_constraintEnd_toStartOf

把A的end position放在B的start position(结束位置相对起始位置对齐)

app:layout_constraintEnd_toEndOf

把A的end position放在B的end position(结束位置对齐)

app:layout_constraintBaseline_toBaselineOf

把A的bottom side放在B的top side(基准线对齐)

来看个例子:

Android xml布局无法预览 android xml布局原理_android_21

<android.support.constraint.ConstraintLayout 
    ...>

    <Button
        android:id="@+id/a"
         ....
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:text="A" />

    <Button
        android:id="@+id/b"
        ....
        app:layout_constraintLeft_toRightOf="@id/a"
        app:layout_constraintTop_toTopOf="@id/a"
        android:text="B" />

    <Button
        android:id="@+id/c"
         ....
        app:layout_constraintLeft_toLeftOf="@id/a"
        app:layout_constraintTop_toBottomOf="@id/a"
        android:text="C" />

    <Button
        android:id="@+id/d"
         ....
        app:layout_constraintLeft_toRightOf="@id/a"
        app:layout_constraintTop_toTopOf="@id/c"
        android:text="D" />
</android.support.constraint.ConstraintLayout>

从中可以看到,

1、layout_constraint*属性的值可以是某个id或者parent(父布局)

2、B要位于A的右边,则使用app:layout_constraintLeft_toRightOf="@id/a",C位于A的下边,则使用app:layout_constraintTop_toBottomOf="@id/a"

对于一个View的边界界定,官方给了下面这张图:

Android xml布局无法预览 android xml布局原理_LinearLayout_22

3.2、约束布局的margin控制

在约束布局中,margin控制需要存在约束关系的才会生效,比如控件A某条边相对于控件B的某条边存在约束关系,则控件A与B之间的margin才会生效。

<?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:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/right_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/color_1"
        android:gravity="center"
        android:text="右上角"
        app:layout_goneMarginStart="20dp"
        android:layout_marginStart="20dp"
        android:textSize="32sp"
        android:visibility="gone"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_min="120dp" />

    <TextView
        android:id="@+id/where"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我在哪里"
        app:layout_goneMarginEnd="20dp"
        android:layout_marginEnd="20dp"
        android:background="#ff0000"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/right_top"/>
</androidx.constraintlayout.widget.ConstraintLayout>

这里使用了两种margin属性,一种就是app:layout_goneMarginEnd,这个只在right_top被gone的时候生效;一种就是android:layout_marginEnd,任何时候都生效。

但是这里的margin需要生效,只有id为where的TextView被id为right_top的TextView所约束,那么id为where的TextView的margin相对于id为right_top的TextView的才生效。而id为right_top的TextView并没有被id为where的TextView约束,所以id为right_top的TextView的margin_start其实看不到生效。这里其实可以认为right_top的margin_start生效了,只不过是相对于parent的左边,但是因为right_top在parent的右上角,所以看不到这个margin效果

3.3、关于view gone

Android xml布局无法预览 android xml布局原理_LinearLayout_23

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    ...>

    <Button
        android:id="@+id/a"
        ...
        android:layout_marginLeft="100dp"
        android:layout_marginTop="20dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/b"
        ...
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        app:layout_constraintLeft_toRightOf="@id/a"
        app:layout_constraintTop_toTopOf="@id/a"
         />

    <Button
        android:id="@+id/c"
       ....
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        app:layout_constraintLeft_toRightOf="@id/b"
        app:layout_constraintTop_toTopOf="@id/b" />
</android.support.constraint.ConstraintLayout>

考虑一个问题,如果B动态设为gone了,C会怎么显示呢?
真实情况如下:

Android xml布局无法预览 android xml布局原理_Android xml布局无法预览_24


为什么会这样显示呢?看他的蓝图应该会好理解些:

Android xml布局无法预览 android xml布局原理_RelativeLayout_25


可以看出,b设为gone之后,他的宽、高、margin都失效了,变为一个点了,但它的constrain还生效,位于指定的位置。c还是可以继续以他为锚点。

那么如何解决关于View gone引起的非预期的布局变化呢?

  • 1、如果可以,尽量使用invisible
  • 2、尽量其他view的布局不依赖会gone的view
  • 3、google也提供了属性layout_goneMargin*=“xdp”,意思是比如当constrainleft的锚点gone时,layout_goneMarginLeft将生效。但因为这个只能设置固定的距离,个人感觉灵活性不是很高。

3.4、居中及bias

一个view如何设置为居中呢?如果查找属性,会发现并没有如RelativeLayout类似的layout_centerVertical属性,那如何设置居中呢?constraint的思想很巧妙。

app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"

根据第一节的知识,大家知道如果设置app:layout_constraintLeft_toLeftOf=“parent”,则view会贴着父view的左边,设置app:layout_constraintRight_toRightOf=“parent” 则会贴着右边,那如果两个都设置,效果会怎样呢?

Android xml布局无法预览 android xml布局原理_RelativeLayout_26


如图,两个都设置,view则会居中。

如果是在两个控件的中间,则可以:

<?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:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/left_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/color_3"
        android:gravity="center"
        android:text="左上角"
        android:textSize="32sp"
        app:layout_constraintStart_toStartOf="@id/root"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_min="120dp" />

    <TextView
        android:id="@+id/right_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/color_1"
        android:gravity="center"
        android:text="右上角"
        app:layout_goneMarginStart="20dp"
        android:layout_marginStart="20dp"
        android:textSize="32sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_min="120dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我在哪里"
        android:background="#ff0000"
        app:layout_constraintLeft_toRightOf="@+id/left_top"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/right_top"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Android xml布局无法预览 android xml布局原理_android_27

至此可以看出,对constraint的理解其实可以看成是像两个弹簧一样,如果只在左边加一个弹簧,右边没有,那左边的势必会把view拉到左边去,如果在右边也加一根弹簧,两个弹簧力相互平衡,则view就居中了。

上面是view居中,如果我想让view向左偏一些,或者位于1/3处该怎么处理?其实也是一样的,想象一下,如果左边的弹簧力大一些,view不是就自然往左偏了嘛。如何使力大一些呢?使用如下属性

  • layout_constraintHorizontal_bias
  • layout_constraintVertical_bias

这两个属性单独使用并不能生效,水平方向的必须要给控件的左右两边都添加约束才会生效,垂直方向的必须要给控件的上下两边都添加约束才可以生效。

bias即偏移量,他们的取值范围从0~1,0即挨着左边,1是挨着右边,所以要使处于1/3处,可以设置如下属性app:layout_constraintHorizontal_bias=“0.33”,效果图如下:

Android xml布局无法预览 android xml布局原理_RelativeLayout_28

实例:

<?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:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button13"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        app:layout_constraintHorizontal_bias="0.4"
        app:layout_constraintVertical_bias="0"
        app:layout_constraintLeft_toRightOf="@+id/left_top"
        app:layout_constraintRight_toRightOf="parent"
        tools:layout_editor_absoluteX="120dp"
        tools:layout_editor_absoluteY="189dp" />

    <TextView
        android:id="@+id/left_top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/color_3"
        android:gravity="center"
        android:text="左上角"
        android:textSize="32sp"
        app:layout_constraintStart_toStartOf="@id/root"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintWidth_min="120dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

比如这个例子,Button的左边被TextView的右边约束,Button的右边被parent约束,这样Button的左右两边都有约束,那么给Button添加水平方向的bias属性就可以生效,即根据Button的左边约束偏移一定的比例,这里就是相对于TextView的右边位置偏移40%。
bias的偏移,是根据控件的水平或者垂直方向的剩余位置的百分比来偏移。

3.5、view的尺寸

设置view的大小除了传统的wrap_content、指定尺寸、match_parent(虽然官方不推荐使用match_parent)外,还可以设置为0dp(官方取名叫MATCH_CONSTRAINT),0dp在constraint可不是指大小是0dp,而是有特殊含义的。他的作用会随着不同的设置有不同的含义:

layout_constraintWidth_default,有三个取值,作用如下:

  • 1、spread,默认值,意思是占用所有的符合约束的空间
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
  ...>

    <Button
        android:id="@+id/a"
        android:layout_width="0dp"
        ...
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"/>

</android.support.constraint.ConstraintLayout>

Android xml布局无法预览 android xml布局原理_RelativeLayout_29


可以看到layout_width为0dp,实际的效果则是宽度和约束一样,左右两边的留白是margin的效果。

2、percent,意思是按照父布局的百分比设置,需要layout_constraintWidth_percent设置百分比例

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout >

    <android.support.constraint.ConstraintLayout
        android:layout_width="300dp"
        android:layout_height="400dp"
        app:layout_constraintHorizontal_bias="0.3"
        >

        <Button
            android:id="@+id/a"
            android:layout_width="0dp"
            ...
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintWidth_default="percent"
            app:layout_constraintWidth_percent="0.4" />
    </android.support.constraint.ConstraintLayout>

</android.support.constraint.ConstraintLayout>

Android xml布局无法预览 android xml布局原理_LinearLayout_30


A的宽度设为0.4,则其宽度为父布局的0.4倍。另外,设置了layout_constraintWidth_percent属性,可以不用指定layout_constraintWidth_default,他会自动设置为percent

3、wrap,意思匹配内容大小但不超过约束限制,注意和直接指定宽度为wrap_content的区别就是不超过约束限制,如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    ...>


    <Button
        android:id="@+id/a"
        ...
        app:layout_constraintLeft_toLeftOf="parent" />
    <Button
        android:id="@+id/c"
        ...
        app:layout_constraintRight_toRightOf="parent" />

    <Button
        android:id="@+id/b"
        android:layout_width="0dp"
         ...
        app:layout_constraintWidth_default="wrap"
        app:layout_constraintLeft_toRightOf="@id/a"
        app:layout_constraintRight_toLeftOf="@id/c"/>

    <Button
        android:id="@+id/d"
        android:layout_width="wrap_content"
        ...
        app:layout_constraintTop_toBottomOf="@id/b"
        app:layout_constraintLeft_toRightOf="@id/a"
        app:layout_constraintRight_toLeftOf="@id/c"/>

</android.support.constraint.ConstraintLayout>

Android xml布局无法预览 android xml布局原理_Android xml布局无法预览_31


可以看到虽然文字很长,但第一行的绿色button宽度达到约束时,就不在增加,而第二行的button显示了完整的内容,超过约束的限制。

ratio
layout_constraintDimensionRatio,即宽和高成一定的比例,其值可以是"width:height"的形式,也可以是width/height的值。该属性生效的前提:宽和高其中有一项为0dp,有constraint。下面按照有几个0dp来分别介绍下:

  • 如果只有一项为0dp,则该项值按照比例计算出来。比如高为20dp,宽为0dp,radio为"2:1",则最终宽为40dp
  • 如果两项都为0dp,则尺寸会设置为满足约束的最大值并保持比例。因为这是系统计算的,有的时候不是我们想要的,我们也可以通过在前面加H、W来指定是哪一个边需要计算。例如"H,2:1",则是指宽度匹配约束,高度是宽度的1/2

max min
有如下属性可以设置其的最大最小值,含义如字面值一样:

layout_constraintWidth_min
layout_constraintWidth_max
layout_constraintHeight_max
layout_constraintHeight_min

在ConstraintLayout中给控件设置min的宽度和高度,必须是要控件的layout_width或者layout_height为wrap_content或者0dp时。给控件设置max的宽度或者高度的时候,必须是要控件的layout_width或者layout_height为0dp时。

不过在设置max的时候需要注意一点,如果单纯只是设置了0dp,如果没给控件添加对应的左右约束或者上下约束,比如没有设置左右约束,那么layout_constraintWidth_max设置的再大也不会超过整个控件的wrap_content的长度。

Android xml布局无法预览 android xml布局原理_RelativeLayout_32


这里的绿色区域的控件,宽度显示的明显没有达到550dp,但是也不会继续变长了。

如果是设置了左右约束,那么最大宽度的限制也就是左右两个约束中间的剩余空间宽度

Android xml布局无法预览 android xml布局原理_android_33

3.6、Guideline和Barrier

1. GuideLine

即参考线的意思,有水平参考线和竖直参考线两种。他的作用就像是一个虚拟的参考线,只是用来方便其他View以他为锚点来布局。

如上一篇所了解到的,ConstraintLayout 的定位原则就是一个View参考其他View的相对布局,如果有的时候当前布局没有合适的参考View,而建一个专门用于定位的View又会太重,这种情况正是GuideLine的用武之地。

例如:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    ...>

    <android.support.constraint.Guideline
        android:id="@+id/guideline"
        ...
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.33" />

    <android.support.constraint.Guideline
        android:id="@+id/guideline2"
        ...
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="130dp" />

    <Button
        ...
        app:layout_constraintLeft_toLeftOf="@id/guideline"
        app:layout_constraintTop_toTopOf="@id/guideline2" />


</android.support.constraint.ConstraintLayout>

Android xml布局无法预览 android xml布局原理_LinearLayout_34


可以看到我分别添加了一个水平参考线和竖直参考线,之后的Button的布局就参考与这两个参考线,而在布局中并不会显示。

Guideline的大部分的属性如layout_width都是不会生效的,而他的位置的确定是由下面三个属性之一来确定的:

  • layout_constraintGuide_begin:距离父布局的左边或者上边多大距离
  • layout_constraintGuide_end:距离父布局的右边或者下边多大距离
  • layout_constraintGuide_percent:百分比,0~1,距离父布局的左边或者上边占父布局的比例

2. Barrier

屏障,一个虚拟View。他主要解决下面遇到的问题:

Android xml布局无法预览 android xml布局原理_android_35


如上图布局,两个TextView,一个button位于他们的右边。现在button设置的是在下面TextView的右边。假设有时候上面的TextView文本变长了,则布局会变为下面这个样子:

Android xml布局无法预览 android xml布局原理_LinearLayout_36


上面的TextView和Button重叠了。这时该怎么解决这个问题呢?Button只能设置一个View作为锚点,设置了上面就顾不了下面了。。。

所以就诞生了Barrier,他可以设置N个View作为锚点,使用方式如下:

<android.support.constraint.Barrier
              android:id="@+id/barrier"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              app:barrierDirection="end"//end,left,right,top,bottom
              app:constraint_referenced_ids="text1,text2" />

则Barrier始终位于text1,text2两个View最大宽度的右边,示意图如下:

Android xml布局无法预览 android xml布局原理_LinearLayout_37

3.Guideline和Barrier的区别

这两个都是线条,都是辅助约束的,但是这两个有一点区别,就是当控件比如出现切换手机语言,而造成控件上的文本显示长度出现变化的时候,Guideline并不会随着控件的长度变化而变化,这样就会造成约束不灵活,而Barrier可以根据控件的宽高变化,灵活移动位置。
所以控件宽高是随着内容动态变化的,建议使用Barrier,如果控件的内容是不变的,可以使用Guideline。

参考

1、https://www.jianshu.com/p/e1f65e094b24 4
2、https://www.jianshu.com/p/13dc7ad66eb1
3、https://www.runoob.com/w3cnote/android-tutorial-relativelayout.html
4、https://www.jianshu.com/p/502127a493fb