约束布局ConstraintLayout是Android Studio 2.2推出的新布局,并从Android Studio 2.3开始成为默认布局文件的根布局,由此可见Android官方对其寄予厚望,那么约束布局究竟具备哪些激动人心的特性呢?

传统的布局如线性布局LinearLayout、相对布局RelativeLayout等等,若要描绘不规则的复杂界面,往往需要进行多重的布局嵌套,不但僵硬死板缺乏灵活性,并且嵌套过多拖慢页面渲染速度。约束布局正是为了解决这些问题应运而生,它兼顾灵活性和高效率,可以看作是相对布局的升级版,在很大程度上改善了Android的用户体验。开发者使用约束布局之时,有多种手段往该布局内添加和拖动控件,既能像原型设计软件AxureRP那样在画板上任意拖曳控件,也能像传统布局那样在XML文件中调整控件布局,还能在代码中动态修改控件对象的位置状态,下面分别介绍约束布局的这几种使用方式:

在画板上拖曳控件

设计师通过工具软件三两下就勾勒出界面原型,程序员却得一个控件一个控件地小心布局,并对控件位置不断微调以符合原型上的尺寸比例。Android原先的界面手工编码一直为人所诟病,因为“所见即所得”才是界面编码的理想方式,比如iOS很早就在Xcode中集成了故事板,使得iOS程序员能够像设计师那样在画板上拖动控件,从而加快了界面编码的工作效率。自从ConstraintLayout诞生之后,Android程序员终于跟上时代步伐,也能在约束布局内部随意拖曳控件,同时存在主从关系的控件之间,附庸控件会跟随目标控件一起移动,从而省却了界面微调的大量劳动。
画板上的控件拖动操作,三言两语说不清楚,还是观看具体的动图比较一目了然:

在XML文件中调整控件布局

传统布局如线性布局、相对布局基本是在XML文件中手工添加控件节点,约束布局当然也允许在布局文件中指定控件的相对位置,这跟相对布局内部的控件位置调整类似,只不过用来表示位置的属性换了个名字罢了。与控制方位有关的属性说明如下所示:
layout_constraintTop_toTopOf : 该控件的顶部与另一个控件的顶部对齐
layout_constraintTop_toBottompOf : 该控件的顶部与另一个控件的底部对齐
layout_constraintBottom_toTopOf : 该控件的底部与另一个控件的顶部对齐
layout_constraintBottom_toBottomOf : 该控件的底部与另一个控件的底部对齐
layout_constraintLeft_toLeftOf : 该控件的左侧与另一个控件的左侧对齐
layout_constraintLeft_toRightOf : 该控件的左侧与另一个控件的右侧对齐
layout_constraintRight_toLeftOf : 该控件的右侧与另一个控件的左侧对齐
layout_constraintRight_toRightOf : 该控件的右侧与另一个控件的右侧对齐
下面是一个运用约束布局的XML文件例子:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/cl_content"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/tv_first"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintTop_toTopOf="parent"android:layout_marginTop="40dp"app:layout_constraintLeft_toLeftOf="parent"android:layout_marginLeft="200dp"android:background="@color/blue"android:text="我是山大王"android:textSize="17sp"android:textColor="@color/black" /><TextViewandroid:id="@+id/tv_second"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="40dp"app:layout_constraintTop_toBottomOf="@+id/tv_first"android:layout_marginLeft="20dp"app:layout_constraintLeft_toLeftOf="@+id/tv_first"android:background="@color/blue"android:text="我是巡山的小喽啰"android:textSize="17sp"android:textColor="@color/black" />
</android.support.constraint.ConstraintLayout>

与该布局文件对应的效果界面如下图所示:

在代码中添加控件

若要利用代码给约束布局动态添加控件,则可照常调用addView方法,不同之处在于,新控件的布局参数必须使用约束布局的布局参数,即ConstraintLayout.LayoutParams,该参数通过setMargins/setMarginStart/setMarginEnd方法设置新控件与周围控件的间距,至于新控件与周围控件的位置约束关系,则可参照ConstraintLayout.LayoutParams的下列属性说明:
topToTop : 当前控件的顶部与指定ID的控件顶部对齐
topToBottom : 当前控件的顶部与指定ID的控件底部对齐
bottomToTop : 当前控件的底部与指定ID的控件顶部对齐
bottomToBottom : 当前控件的底部与指定ID的控件底部对齐
startToStart : 当前控件的左侧与指定ID的控件左侧对齐
startToEnd : 当前控件的左侧与指定ID的控件右侧对齐
endToStart : 当前控件的右侧与指定ID的控件左侧对齐
endToEnd : 当前控件的右侧与指定ID的控件右侧对齐
下面是在约束布局中添加新控件的代码例子:

private void addNewView() {TextView tv = new TextView(this);tv.setText("长按删除该文本");tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);tv.setGravity(Gravity.CENTER);tv.setBackgroundColor(Color.YELLOW);ConstraintLayout.LayoutParams container = new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,ConstraintLayout.LayoutParams.WRAP_CONTENT);//设置控件左侧与另一个控件的左侧对齐//水平方向上只能使用start和end,因为left和right可能无法奏效container.startToStart = mLastViewId;//设置控件顶部与另一个控件的底部对齐container.topToBottom = mLastViewId;container.setMargins(0, Utils.dip2px(this, 30), 0, 0);//左侧间距要使用Start,不能用Left,因为set.applyTo方法会清空Left的间距container.setMarginStart(Utils.dip2px(this, 10));tv.setLayoutParams(container);tv.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View vv) {cl_content.removeView(vv);return true;}});mLastViewId += 1000;tv.setId(mLastViewId);cl_content.addView(tv);
}

添加新控件的效果动图如下所示:

在代码中动态调整控件位置

有时根据用户在界面上的操作,需要立即调整相关控件的显示位置,这要在代码中修改控件的位置参数。既然添加控件时可以通过布局参数指定控件位置,那么调整控件位置一样也可以通过布局参数来实现,基本流程依次为:先调用getLayoutParams方法获得当前的布局参数->再指定新的控件约束关系及间距->最后调用setLayoutParams启用新的布局参数。
可是按照传统的布局参数方式存在诸多不便之处,比如以下几点就很不合理:
1、控件约束关系的指定,与间距设定是分开的,其他人难以找到二者之间的对应关系;
2、setMargins方法同时设置上下左右四个方向的间距,无法单独设置某个方向的间距;
3、布局参数在启用时立即生效,没有渐变的过程,让用户觉得很突兀。从下面的动图就看到这个位置一下子发生变化,用户体验很不好:

为了改进以上几个问题,constraint-layout开发包从1.0.1本版开始,增加了新的约束设置类ConstraintSet,该工具针对这几个问题分别给出了相应的解决方案:
1、提供connect方法,一次性指定存在约束关系的两个控件,以及它们的间距;
2、提供setMargin方法,允许单独设置上下左右某个方向的间距;
3、提供了渐变管理类TransitionManager,支持展示空间位置变化的切换动画;
下面是使用ConstraintSet修改控件位置的具体代码:

private void moveView() {//使用动画展示新旧约束关系的切换过程。如果删掉这行则不展示切换动画TransitionManager.beginDelayedTransition(cl_content);int margin = Utils.dip2px(this, isMoved?200:20);//需要下载最新的constraint-layout,才能使用ConstraintSetConstraintSet set = new ConstraintSet();//复制原有的约束关系set.clone(cl_content);//清空该控件的约束关系//set.clear(tv_first.getId());//设置该控件的约束宽度//set.constrainWidth(tv_first.getId(), ConstraintLayout.LayoutParams.WRAP_CONTENT);//设置该控件的约束高度//set.constrainHeight(tv_first.getId(),ConstraintLayout.LayoutParams.WRAP_CONTENT);//设置该控件的顶部约束关系与间距//set.connect(tv_first.getId(), ConstraintSet.TOP, cl_content.getId(), ConstraintSet.BOTTOM, margin);//设置该控件的底部约束关系与间距//set.connect(tv_first.getId(), ConstraintSet.BOTTOM, cl_content.getId(), ConstraintSet.BOTTOM, margin);//设置该控件的左侧约束关系与间距set.connect(tv_first.getId(), ConstraintSet.START, cl_content.getId(), ConstraintSet.START, margin);//设置该控件的右侧约束关系与间距//set.connect(tv_first.getId(), ConstraintSet.END, cl_content.getId(), ConstraintSet.END, margin);//LEFT和RIGHT的margin不管用,只有START和END的margin才管用//set.setMargin(tv_init.getId(), ConstraintSet.START, 200);//启用新的约束关系set.applyTo(cl_content);isMoved = !isMoved;
}

上述变更控件位置代码的对应效果图如下所示,有了切换动画这下看起来比较柔和了:

点此查看Android开发笔记的完整目录

__________________________________________________________________________
本文现已同步发布到微信公众号“老欧说安卓”,打开微信扫一扫下面的二维码,或者直接搜索公众号“老欧说安卓”添加关注,更快更方便地阅读技术干货。

Android开发笔记(一百四十九)约束布局ConstraintLayout相关推荐

  1. Android开发笔记(八十九)单例模式

    基本概念 单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,从而方便对实例个数的控制并节约系统资源. 单例模式有三个特点: 1.某个类只能有一个实例: 2.它要自行创建这个实例: 3.它只有 ...

  2. Android开发笔记(七十九)资源与权限校验

    硬件资源 因为移动设备的硬件配置各不相同,为了防止使用了不存在的设备资源,所以要对设备的硬件情况进行检查.一般情况下,前置摄像头.部分传感器在低端手机上是没有的,像SD卡也可能因为用户没插卡使得找不到 ...

  3. Android开发笔记(五十九)巧用传感器

    传感器Sensor 传感器是Android用来感知周围环境以及运动信息的工具.因为具体的感应信息依赖于相关硬件,所以虽然Android提供了众多的感应器,但不是每部手机都能支持这么多感应器,恰恰相反, ...

  4. Android开发笔记(七十四)布局文件优化

    include/merge 布局优化中常常用到include/merge标签,include的含义类似C代码中的include,意思是直接把指定布局片段包含进当前的布局文件.include适用于多个布 ...

  5. Android开发笔记(六十九)JNI实战

    NDK NDK的用途 NDK全称为Native Development Kit,意即原生的开发工具,NDK允许开发者在APP中通过C/C++代码执行部分程序.它是Android提供的方便开发者通过JN ...

  6. Android开发笔记(四十九)异步任务处理AsyncTask

    AsyncTask异步任务 Thread+Handler方式虽然能够实现多线程的通信处理,但是写起代码来颇为繁琐,所以Android提供了AsyncTask这样一个轻量级的异步任务类,其内部封装好Th ...

  7. Android开发笔记(三十九)Activity的生命周期

    与生命周期有关的方法 下面是Activity类与生命周期有关的方法: onCreate : 创建页面 onStart : 开始页面 onStop : 停止页面 onResume : 恢复页面 onPa ...

  8. Android开发笔记(二十九)使用SharedPreferences存取数据

    SharedPreferences使用场景 共享参数(SharedPreferences)是Android上的一个轻量级存储工具,存储结构是类似map的key-value键值对形式.它主要用于保存ap ...

  9. Android开发笔记(八十六)几个特殊的类

    接口interface interface是一些功能的集合,但它只定义了对象必须实现的成员,而不包含成员的实现代码,成员的具体代码由实现接口的类提供.Android对接口的使用场景主要有三类:事件监听 ...

  10. Android开发笔记(八十)运行状态检查

    大家都知道刻舟求剑的寓言故事,说的是事物是发展变化着的,如果拘泥于原来的情况,那随着情况的改变,就不会得到预期的结果.同样,影响app运行的因素,并不只是外部环境(如硬件.系统.权限等等),还包括ap ...

最新文章

  1. 《IT项目管理》读书笔记(9) —— 项目沟通管理
  2. 天池 在线编程 两句话中的不常见单词(哈希计数)
  3. 编辑电线标注及图纸上从主电源线上引出多条支路时如何进行线号的编写?
  4. 博客园的祥和需要大家共同努力
  5. 十一这里最好玩啦!快来一起玩耍!
  6. python如何截长图_python 截长图、H5页面截长图
  7. 程序员在跳槽时,该怎么说离职原因?
  8. 美团店铺评价语言处理以及分类(tfidf,SVM,决策树,随机森林,Knn,ensemble)...
  9. 两个时间之间的有多少个工作日,有多少天,有多少天的周末
  10. A股上市公司财报披露时间
  11. [云原生专题-29]:K8S - 核心概念 - 名字空间/命名空间概念详解与主要操作案例
  12. android.view.InflateException: Binary XML file line #22: Binary XML file line #22:: Error inflating
  13. Modelsim使用方法
  14. 有趣的Shellcode和栈
  15. 小米4C 安装Xposed框架失败解决方案
  16. 2020计网实验报告
  17. 动物科学可以转计算机专业吗,报考华中农业大学计算机,被调剂到动物科学,真不该盲目服从...
  18. win10中chrome浏览器书签路径
  19. 动作一键制作ps信号干扰故障效果
  20. 20141226-网易有道实习反思

热门文章

  1. 蓝桥杯 基础练习 回文数
  2. 吴恩达机器学习ex7:K-means分类
  3. P3007 [USACO11JAN]大陆议会The Continental Cowngress(2-SAT)
  4. Spring配置跨域请求
  5. Java自动化测试框架-07 - TestNG之Factory篇 - (详细教程)
  6. l开头的英文车标是什么车_行业冷知识 | 为什么汽车品牌都喜欢用动物做车标?...
  7. 二分归并排序算法_02 算法推送归并排序
  8. odis工程师一键导入导出匹配数据信息功能_机械重复做了这么久,才发现竟然可以批量用户导入导出...
  9. 使用C++ 调用 C# 程序
  10. android异步工作,Android异步消息机制详解