android 约束布局的动态添加

使用方法

//获取constraintLayout 容器里的原先的约束条件
val constraintSet = ConstraintSet().apply { this.clone(cl_container) }
//用两个控件的id添加添加约束条件
constraintSet.connect(tvTitle.id, ConstraintSet.START, cl_container.id , ConstraintSet.START) 
//应用到布局的约束布局中
constraintSet.applyTo(cl_container)

1.简单归简单,但是也容易犯错。在此mark一下
比如我想根据一个map的内容动态添加一个 title: value这样的布局:
这时候就要注意:
每次循环时都要用constraintSet.clone()方法获取原来的约束条件,即不能够把这个方法调用放到循环外,然后再拿他的引用在循环里调用...不然每一次新的循环都会把上一次的约束条件覆盖掉。
错误演示:

val constraintSet = ConstraintSet().apply { this.clone(cl_container) }
map.forEach { entry ->
//每次循环拿到的其实是布局xml文件里的约束条件,而不是每次动态添加进去后新的约束条件
constraintSet.connect(tvTitle.id, ConstraintSet.START, cl_container.id , ConstraintSet.START)   
applyTo(cl_container)

2.constrainSet的获取一定要在addView的后面,不然会无法约束,造成视图消失

addView(View(this))
val constraintSet = ConstraintSet().apply { this.clone(cl_container) }

4/12更新:什么傻X东西还要按顺序调用!

关于addView和ConstraintSet().clone的调用顺序关系,实在是太奇葩了,一不留神就没留意顺序,导致约束失败,控件消失,所以我决定看一下源码...
以下是clone()的源码

  public void clone(ConstraintLayout constraintLayout) {
        int count = constraintLayout.getChildCount();
        this.mConstraints.clear();
        //拿出子view遍历
        for(int i = 0; i < count; ++i) {
            View view = constraintLayout.getChildAt(i);
            LayoutParams param = (LayoutParams)view.getLayoutParams();
            int id = view.getId();
            if (id == -1) {
                throw new RuntimeException("All children of ConstraintLayout must have ids to use ConstraintSet");
          //把子viewId作为key,约束属性(ConstraintSet)作为value,放到一个map里
            if (!this.mConstraints.containsKey(id)) {
                this.mConstraints.put(id, new ConstraintSet.Constraint());
            ConstraintSet.Constraint constraint = (ConstraintSet.Constraint)this.mConstraints.get(id);
            constraint.fillFrom(id, param);
            //设置能见度
            constraint.visibility = view.getVisibility();

等等,你们发现什么没。这个clone竟然会拿到当前约束布局所有的子view,然后设置每一个的约束参数的visibility...也就是说,如果你没在clone前将子view add进去,你就相当于你新建的view都没有设置可见性...
那我们最后不是会调用apply方法,把所有动态添加的约束参数应用到约束布局中吗。难道在调用这个apply方法就不会重新设置可见度么
以下为apply方法代码

 void applyToInternal(ConstraintLayout constraintLayout) {
        int count = constraintLayout.getChildCount();
        HashSet<Integer> used = new HashSet(this.mConstraints.keySet());
        LayoutParams param;
        for(int i = 0; i < count; ++i) {
            View view = constraintLayout.getChildAt(i);
            int id = view.getId();
            if (id == -1) {
                throw new RuntimeException("All children of ConstraintLayout must have ids to use ConstraintSet");
            if (this.mConstraints.containsKey(id)) {
                used.remove(id);
                ConstraintSet.Constraint constraint = (ConstraintSet.Constraint)this.mConstraints.get(id);
                param = (LayoutParams)view.getLayoutParams();
                constraint.applyTo(param);
                view.setLayoutParams(param);
                //设置view可见性
                view.setVisibility(constraint.visibility);