专栏首页老欧说安卓Android开发笔记(一百四十九)约束布局ConstraintLayout

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

约束布局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">

    <TextView
        android: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" />

    <TextView
        android: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() {
        @Override
        public 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,才能使用ConstraintSet
    ConstraintSet 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开发笔记的完整目录

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android开发笔记(一百五十三)OpenGL绘制三维图形的流程

    从这篇文章开始,接下来会连载一系列的OpenGL相关博文,好好探讨如何在Android中进行OpenGL开发。 OpenG...

    用户4464237
  • Android5.0和6.0之后新增的控件说明

    Android自5.0后增加了不少新控件,帮助开发者实现了更酷更炫的UI效果。可是对于初学者来说,这些新控件的用法不像老控件那...

    用户4464237
  • 《Android Studio开发实战 从零基础到App上线》第一版的资源下载和内容勘误

    下面是《Android Studio开发实战 从零基础到App上线》(第一版)一书用到的工具和代码资源: 1、本书使用的Android Studio版本为2....

    用户4464237
  • Qt编写控件属性设计器1-加载插件

    加载插件是整个属性设计器的第一步要打通的功能,插件中的控件都加载不了,后面就别搞别玩下去了没法玩的,要从一个动态库中加载出来控件,肯定需要用到反射机制,以前做....

    feiyangqingyun
  • IE6查看ActiveX控件是否已经安装以及版本号

    最近经常要处理控件问题,ie6+可以直接Internet选项,程序中看到已经安装的控件以及版本号等信息,万恶的ie6确看到不到控件的具体版本信息。

    西门呀在吹雪
  • C# C/S控件库HZHControls使用指南

    HZHControls是一个基于.Net Framework4.0,扁平化的、漂亮的、开源的C/S控件库,这是官网的介绍,也就是作者专门开发的一套可以在C/S客...

    zls365
  • 动态控件的新思路

            常常有如此感叹:动态控件好加,但是状态维持困难。就是说,加入动态控件很容易,关联处理事件也不难,但是对于控件的状态把握,却很麻烦。往往需要在lo...

    用户1075292
  • 动态加载控件

    参考文章:http://blog.csdn.net/yicko/archive/2005/04/16/349740.aspx 1、加载的是普通的控件,不是用户控...

    用户1075292
  • 当iOS遇见UI

    iOS应用开发的一项内容就是用户界面的开发。不管应用程序实际包含的逻辑有多复杂和优秀,如果这个应用没有提供友好的图形用户界面,那么也很难吸引最终用户。相反,如果...

    博文视点Broadview
  • 【程序源代码】Qt编写的一些开源的demo

    今天给大家讲一讲:Qt编写的一些开源的demo。内容相对比较简单 大家按照如下步骤进行操作就可以了。

    程序源代码

扫码关注云+社区

领取腾讯云代金券