Android中整个的View的组装是采用组合模式。

ViewGroup就相当与树根,各种Layout就相当于枝干,各种子View,就相当于树叶。

至于View类。我们就当它是个种子吧。哈哈!

ViewGroup属于树根,可以生长数很多枝干(继承自定义Layout)而枝干上有可以长出很多叶子(TextView,ImageVIew......)

好,闲话少叙,接下来步入正题!

首先,关于View的操作方法,被定义在一个叫做ViewManager的接口中,接口中还有两个方法,分别是移除和更新,这次主要分析addView

public interface ViewManager
    {
        /**
         * Assign the passed LayoutParams to the passed View and add the view to the window.
         * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
         * errors, such as adding a second view to a window without removing the first view.
         * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
         * secondary {@link Display} and the specified display can't be found
         * (see {@link android.app.Presentation}).
         * @param view The view to be added to this window.
         * @param params The LayoutParams to assign to view.
         */
        public void addView(View view, ViewGroup.LayoutParams params);
        public void updateViewLayout(View view, ViewGroup.LayoutParams params);
        public void removeView(View view)
  }

addView在ViewGroup中实现,接下来贴出代码进行分析

    public void addView(View child, LayoutParams params) {
        addView(child, -1, params);
    }

这个方法中调用了自身的addView方法,并且多传递了一个-1,这个-1是View的索引,要插入的位置

    public void addView(View child, int index, LayoutParams params) {
        if (DBG) {
            System.out.println(this + " addView");
        }

        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }

        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
        // therefore, we call requestLayout() on ourselves before, so that the child's request
        // will be blocked at our level
        requestLayout();
        invalidate(true);
        addViewInner(child, index, params, false);
    }

这个方法先是重绘了一下布局,然后调用了addViewInner(child, index, params, false);方法,来把View插入到相应的位置

    private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {

        if (mTransition != null) {
            // Don't prevent other add transitions from completing, but cancel remove
            // transitions to let them complete the process before we add to the container
            mTransition.cancel(LayoutTransition.DISAPPEARING);
        }

        if (child.getParent() != null) {
            throw new IllegalStateException("The specified child already has a parent. " +
                    "You must call removeView() on the child's parent first.");
        }

        if (mTransition != null) {
            mTransition.addChild(this, child);
        }

        if (!checkLayoutParams(params)) {
            params = generateLayoutParams(params);
        }

        if (preventRequestLayout) {
            child.mLayoutParams = params;
        } else {
            child.setLayoutParams(params);
        }

        if (index < 0) {
            index = mChildrenCount;
        }

        //ViewGroup用一个View类型的数组去维护下边的子view,这个方法就是把view添加到响应的位置上
        addInArray(child, index);

        //绑定插入的view的父容器为当前group
        // tell our children
        if (preventRequestLayout) {
            child.assignParent(this);
        } else {
            child.mParent = this;
        }

        if (child.hasFocus()) {
            requestChildFocus(child, child.findFocus());
        }

        AttachInfo ai = mAttachInfo;
        if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
            boolean lastKeepOn = ai.mKeepScreenOn;
            ai.mKeepScreenOn = false;
            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
            if (ai.mKeepScreenOn) {
                needGlobalAttributesUpdate(true);
            }
            ai.mKeepScreenOn = lastKeepOn;
        }

        if (child.isLayoutDirectionInherited()) {
            child.resetRtlProperties();
        }

        dispatchViewAdded(child);

        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
        }

        if (child.hasTransientState()) {
            childHasTransientStateChanged(child, true);
        }

        if (child.getVisibility() != View.GONE) {
            notifySubtreeAccessibilityStateChangedIfNeeded();
        }

        if (mTransientIndices != null) {
            final int transientCount = mTransientIndices.size();
            for (int i = 0; i < transientCount; ++i) {
                final int oldIndex = mTransientIndices.get(i);
                if (index <= oldIndex) {
                    mTransientIndices.set(i, oldIndex + 1);
                }
            }
        }
    }

这个方法先检查一下LayoutParams,看看插入的该view是否存在长宽,如果没有就生成一个默认的LayoutParams

然后判断index是否小于0,小于0就赋值为当前容器中的个数,代表插入到最后一项

随后就调用addInArray方法,来插入View到当前ViewGroup中,插入完成后给该view绑定一下父容器(getParent的值)

随后就是一些焦点,监听的分发,我们仔细分析一下插入方法就好了,就是addInArray

   private void addInArray(View child, int index) {
        View[] children = mChildren;
        final int count = mChildrenCount;
        final int size = children.length;
        if (index == count) {
            if (size == count) {
                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
                System.arraycopy(children, 0, mChildren, 0, size);
                children = mChildren;
            }
            children[mChildrenCount++] = child;
        } else if (index < count) {
            if (size == count) {
                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
                System.arraycopy(children, 0, mChildren, 0, index);
                System.arraycopy(children, index, mChildren, index + 1, count - index);
                children = mChildren;
            } else {
                System.arraycopy(children, index, children, index + 1, count - index);
            }
            children[index] = child;
            mChildrenCount++;
            if (mLastTouchDownIndex >= index) {
                mLastTouchDownIndex++;
            }
        } else {
            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
        }
    }

mChildren是ViewGroup中的一个View类型的数组,里边存放了该Group下的所有子View

ARRAY_CAPACITY_INCREMENT这个常量的值是12

首先判断一下mChildren的位置还是否充足,不充足就继续扩充12个位置出来,copy源数组内容进到新数组里,然后再把本次要添加的view放到最后

如果index比count小,说明是插入操作,也是先判断位置是否充足,不充足就扩充并且copy到index处,然后在把剩下的copy到index+1到末尾

这样就把index位置空了出来。就完成了插入操作

插入完成后,系统会重绘界面,你就可以看到你插入的view了。

addView这个方法就分析完了,有什么疑问就可以指出来,错误也一样。共同学习共同进步。

[Android FrameWork 6.0源码学习] ViewGroup的addView函数分析的更多相关文章

  1. [Android FrameWork 6.0源码学习] View的重绘过程之WindowManager的addView方法

    博客首页:http://www.cnblogs.com/kezhuang/p/关于Activity的contentView的构建过程,我在我的博客中已经分析过了,不了解的可以去看一下<[Andr ...

  2. [Android FrameWork 6.0源码学习] LayoutInflater 类分析

    LayoutInflater是用来解析XML布局文件,然后生成对象的ViewTree的工具类.是这个工具类的存在,才能让我们写起Layout来那么省劲. 我们接下来进去刨析,看看里边的奥秘 //调用i ...

  3. [Android FrameWork 6.0源码学习] View的重绘过程之Draw

    View绘制的三部曲,测量,布局,绘画现在我们分析绘画部分测量和布局 在前两篇文章中已经分析过了.不了解的可以去我的博客里找一下 下面进入正题,开始分析调用以及函数原理 private void pe ...

  4. [Android FrameWork 6.0源码学习] View的重绘过程

    View绘制的三部曲,  测量,布局,绘画今天我们分析测量过程 view的测量是从ViewRootImpl发起的,View需要重绘,都是发送请求给ViewRootImpl,然后他组织重绘在重绘的过程中 ...

  5. [Android FrameWork 6.0源码学习] Window窗口类分析

    了解这一章节,需要先了解LayoutInflater这个工具类,我以前分析过:http://www.cnblogs.com/kezhuang/p/6978783.html Window是Activit ...

  6. [Android FrameWork 6.0源码学习] View的重绘过程之Layout

    View绘制的三部曲,测量,布局,绘画现在我们分析布局部分测量部分在上篇文章中已经分析过了.不了解的可以去我的博客里找一下 View的布局和测量一样,都是从ViewRootImpl中发起,ViewRo ...

  7. [Android FrameWork 6.0源码学习] View的重绘ViewRootImpl的setView方法

    博客首页:http://www.cnblogs.com/kezhuang/p/ 本篇文章来分析一下WindowManager的后续工作,也就是ViewRootImpl的setView函数的工作 /i* ...

  8. [Android framework学习] ViewGroup的addView函数分析

    博客首页:http://www.cnblogs.com/kezhuang/p/ Android中整个的View的组装是采用组合模式. ViewGroup就相当与树根,各种Layout就相当于枝干,各种 ...

  9. 【Spark2.0源码学习】-1.概述

          Spark作为当前主流的分布式计算框架,其高效性.通用性.易用性使其得到广泛的关注,本系列博客不会介绍其原理.安装与使用相关知识,将会从源码角度进行深度分析,理解其背后的设计精髓,以便后续 ...

随机推荐

  1. Spring——Web应用中的IoC容器创建(WebApplicationContext根应用上下文的创建过程)

    基于Spring-4.3.7.RELEASE Spring的配置不仅仅局限在XML文件,同样也可以使用Java代码来配置.在这里我使用XML配置文件的方式来粗略地讲讲WebApplicationCon ...

  2. 第一篇:使用Spark探索经典数据集MovieLens

    前言 MovieLens数据集包含多个用户对多部电影的评级数据,也包括电影元数据信息和用户属性信息. 这个数据集经常用来做推荐系统,机器学习算法的测试数据集.尤其在推荐系统领域,很多著名论文都是基于这 ...

  3. nodeJS中的包

    前面的话 Node组织了自身的核心模块,也使得第三方文件模块可以有序地编写和使用.但是在第三方模块中,模块与模块之间仍然是散列在各地的,相互之间不能直接引用.而在模块之外,包和NPM则是将模块联系起来 ...

  4. 微信小程序大全(上)(最新整理 建议收藏)

  5. php的八大数据类型

    1. 八大数据: bool 布尔类型:0,1:真假 integer 整形 float 浮点型 string 字符串 array 数组 object 对象,类,class resource 文件,图片, ...

  6. java 空指针异常造成的原因有哪些

    实际上 空指针异常   也就是你用了一个没有实际值的对象 1. 某一对象没有被实例化,就拿来使用,如调用此对象的方法,会抛空指针异常. 2. 获取从别的对象传过来的对象为空 3. 数据库查询方面的空指 ...

  7. 利刃 MVVMLight 10:Messenger 深入

    1.Messager交互结构和消息类型 衔接上篇,Messeger是信使的意思,顾名思义,他的目是用于View和ViewModel 以及 ViewModel和ViewModel 之间的消息通知和接收. ...

  8. 细说"回车"和"换行"的故事

    引言 最近在php还有c#以及memcache的shell当中经常看到\r\n的写法,刚开始还没注意, 不过后面感觉这样写有些不对头,\r表示回车  \n表示换行,那这样不是换行了两次吗? 为了解决疑 ...

  9. C#操作CAD-初始化、引用dll

    操作cad等方式有很多,比如C,C++,vb.lisp(效率最高,但是语言结构性太差)和C#,因为我等个人习惯和方便等原因,在此讲解一下用C#操作流程,后续会更新操作图层.扩展数据.绘图等操作步骤.当 ...

  10. python学习之爬虫(一) ——————爬取网易云歌词

    接触python也有一段时间了,一提到python,可能大部分pythoner都会想到爬虫,没错,今天我们的话题就是爬虫!作为一个小学生,关于爬虫其实本人也只是略懂,怀着"Done is b ...