Android开发

addView方法分析

2018-04-28  本文已影响111人  gczxbb

ViewGroup的addView方法,Android开发中最常用的方法之一。它是容器视图特有的方法,不在基类View中。该方法向容器中增加子节点,视图树结构中,每个视图节点可以有多个子节点,但是,每个子节点只能有一个父节点。本文主要分析addView方法如何添加子节点,再看一下和它相关的常用方法。在ViewGroup类,它有三个重载方法。

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

index是插入容器内部子视图数组的索引位置,默认<0,插入数组尾部。

public void addView(View child, int index) {
    LayoutParams params = child.getLayoutParams();
    if (params == null) {
        params = generateDefaultLayoutParams();
        if (params == null) {  //抛出异常
        }
    }
    addView(child, index, params);
}

如果布局参数是空,生成默认值,最终的布局参数一定要存在。

protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.WRAP_CONTENT, 
                    LayoutParams.WRAP_CONTENT);
}

宽高是WRAP_CONTENT类型。

public void addView(View child, int index, LayoutParams params) {
    requestLayout();
    invalidate(true);
    addViewInner(child, index, params, false);
}

该方法将依次调用另外三个方法,分别是requestLayout方法、invalidate方法、和addViewInner方法。前两个方法视图重绘,将在其他文章单独分析。

private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {
    //child的mParent存在,抛出异常,一个视图不能加到两个父视图节点
    ...
    //检查生成LayoutParams
    if (!checkLayoutParams(params)) {
        params = generateLayoutParams(params);
    }
    //设置LayoutParams
    if (preventRequestLayout) {
        child.mLayoutParams = params;
    } else {
        child.setLayoutParams(params);
    }
    //index小于0时,插入点在mChildrenCount,即子View数组的尾部
    if (index < 0) {
        index = mChildrenCount;
    }
    //插入数组
    addInArray(child, index);
    //为child设置mParent
    if (preventRequestLayout) {
        child.assignParent(this);
    } else {
        child.mParent = this;
    }
    
    AttachInfo ai = mAttachInfo;
    if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
        child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
        ...
    }
    ...
    dispatchViewAdded(child);
    ...
}

首先,查它子视图内部mParent引用,一个子视图不可以同时挂两个父节点,抛出IllegalStateException异常。
然后,设置子视图内部LayoutParams。addInArray方法,加入内部子视图数组,同时更新数量。设置子视图父节点mParent,即当前容器视图,
最后,子视图dispatchAttachedToWindow方法,初始化内部AttachInfo对象。同时,在该方法中,调用子视图的onAttachedToWindow方法,表示已经attached到窗体。

下面看一下几个类似的方法。
removeView方法。
addViewInLayout方法、removeViewInLayouy方法。
attachViewToParent方法、detachViewFromParent方法。


相似方法分析

removeView方法和addView方法是一对,前者是删除视图,后者是增加视图。

public void removeViewAt(int index) {
    removeViewInternal(index, getChildAt(index));
    requestLayout();
    invalidate(true);
}

删除index索引的视图,和addView一样,该方法也会调用requestLayout方法和invalidate方法,视图重绘。getChildAt方法,根据索引从数组查找视图。

private void removeViewInternal(int index, View view) {
    if (view.getAnimation() != null ||
                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
        addDisappearingView(view);
    } else if (view.mAttachInfo != null) {
        view.dispatchDetachedFromWindow();
    }
    ...
    removeFromArray(index);      
    dispatchViewRemoved(view);    
    ...
}

调用子视图dispatchDetachedFromWindow方法,onDetachedFromWindow方法。子视图主动调用,表示自己detached窗体,可以重写做一些操作。
removeFromArray方法,从数组中删除索引处的视图。

addViewInLayout和removeViewInLayou方法是一对,和前面两个方法类似,也会调用前面的addViewInner和removeViewInternal方法,区别是,不会调用重绘方法。
attachViewToParent和detachViewFromParent方法是一对,它们仅调用数组操作的addInArray和removeFromArray方法,区别是,不会重绘,也不会有子视图的attach和detached回调,仅仅是数据的变化。在RecyclerView源码中会遇到这两个方法,这里就不详细介绍了。


总结

addView和removeView方法,操作容器内的子视图数组,触发视图重绘制,触发子视图attach和detached窗体回调。
addViewInLayout和removeViewInLayou方法,与上面一样,只是不会重绘视图。
attachViewToParent和detachViewFromParent方法,只会操作容器内的子视图数组。


任重而道远

上一篇下一篇

猜你喜欢

热点阅读