自定义view中onSizeChanged、onFinishInflate方法调用时机

转自:http://blog.csdn.net/anhenzhufeng/article/details/72886181

一般自定义View或ViewGroup基本上都会去实现onMeasure、onLayout、onDraw方法
还有另外两个方法是onFinishInflate和onSizeChanged

onFinishInflate

onFinishInflate方法只有在布局文件中加载View实例会回调,如果直接new一个View的话是不会回调的。
如果是一个ViewGroup,只有它和它的子View完全被加载实例化了之后才会回调该ViewGroup的这个方法
因为在LayoutInflater的inflate执行过程中最终的调用路径是:inflate -> rInflateChildren -> rInflate

其中inflate的调用是这样的:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    synchronized (mConstructorArgs) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

        final Context inflaterContext = mContext;
        final AttributeSet attrs = Xml.asAttributeSet(parser);
        Context lastContext = (Context) mConstructorArgs[0];
        mConstructorArgs[0] = inflaterContext;
        View result = root;

        try {
            // Look for the root node.
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG &&
                    type != XmlPullParser.END_DOCUMENT) {
                // Empty
            }

            if (type != XmlPullParser.START_TAG) {
                throw new InflateException(parser.getPositionDescription()
                        + ": No start tag found!");
            }

            final String name = parser.getName();

            if (DEBUG) {
                System.out.println("**************************");
                System.out.println("Creating root view: "
                        + name);
                System.out.println("**************************");
            }

            if (TAG_MERGE.equals(name)) {
                if (root == null || !attachToRoot) {
                    throw new InflateException(" can be used only with a valid "
                            + "ViewGroup root and attachToRoot=true");
                }

                rInflate(parser, root, inflaterContext, attrs, false);
            } else {
                // Temp is the root view that was found in the xml
                final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                ViewGroup.LayoutParams params = null;

                if (root != null) {
                    if (DEBUG) {
                        System.out.println("Creating params from root: " +
                                root);
                    }
                    // Create layout params that match root, if supplied
                    params = root.generateLayoutParams(attrs);
                    if (!attachToRoot) {
                        // Set the layout params for temp if we are not
                        // attaching. (If we are, we use addView, below)
                        temp.setLayoutParams(params);
                    }
                }

                if (DEBUG) {
                    System.out.println("-----> start inflating children");
                }

                // Inflate all children under temp against its context.
                rInflateChildren(parser, temp, attrs, true);

                if (DEBUG) {
                    System.out.println("-----> done inflating children");
                }

                // We are supposed to attach all the views we found (int temp)
                // to root. Do that now.
                if (root != null && attachToRoot) {
                    root.addView(temp, params);
                }

                // Decide whether to return the root that was passed in or the
                // top view found in xml.
                if (root == null || !attachToRoot) {
                    result = temp;
                }
            }

        } catch (XmlPullParserException e) {
            final InflateException ie = new InflateException(e.getMessage(), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } catch (Exception e) {
            final InflateException ie = new InflateException(parser.getPositionDescription()
                    + ": " + e.getMessage(), e);
            ie.setStackTrace(EMPTY_STACK_TRACE);
            throw ie;
        } finally {
            // Don't retain static reference on context.
            mConstructorArgs[0] = lastContext;
            mConstructorArgs[1] = null;

            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        return result;
    }
}

然后,通过递归调用rInflate解析所有的xml节点,如下:

void rInflate(XmlPullParser parser, View parent, Context context,
        AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {

    final int depth = parser.getDepth();
    int type;

    while (((type = parser.next()) != XmlPullParser.END_TAG ||
            parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {

        if (type != XmlPullParser.START_TAG) {
            continue;
        }

        final String name = parser.getName();

        if (TAG_REQUEST_FOCUS.equals(name)) {
            parseRequestFocus(parser, parent);
        } else if (TAG_TAG.equals(name)) {
            parseViewTag(parser, parent, attrs);
        } else if (TAG_INCLUDE.equals(name)) {
            if (parser.getDepth() == 0) {
                throw new InflateException(" cannot be the root element");
            }
            parseInclude(parser, context, parent, attrs);
        } else if (TAG_MERGE.equals(name)) {
            throw new InflateException(" must be the root element");
        } else {
            final View view = createViewFromTag(parent, name, context, attrs);
            final ViewGroup viewGroup = (ViewGroup) parent;
            final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
            rInflateChildren(parser, view, attrs, true);
            viewGroup.addView(view, params);
        }
    }

    if (finishInflate) {
        parent.onFinishInflate();
    }
}

完成后,会有个判断

if (finishInflate) {
    parent.onFinishInflate();
}

这时候通知父控件执行onFinishInflate方法,而此时仅仅是将所有的子控件实例化到内存中,也就是可以通过getChildAt()来获取相应的子控件实例了。
这时候还没执行onMeasure呢,而通过layoutInflate.inflate出来的View或ViewGroup往往是要添加到已有的父控件,
比如使用setContentView方式获取我们的xml文件,其中做了几个步骤:
首先去xml中解析出我们的布局View
然后在把这个View添加到Activity的顶级视图中(即DecorView的子布局content中),添加的过程必然使用到了addView的操作,而该操作就会触发requestLayout和invalidate这两个方法,
这两个方法又必然会触发ViewParent(即ViewRootImpl)这个视图管理者的一系列操作,这一系列操作由performTraversalse开始,顺序去调用performMeasure -> view.measure -> onMeasure,performLayout -> view.layout -> onLayout,performDraw -> draw -> drawSoftWare -> view.draw -> onDraw(实现自身的绘制) -> dispatchDraw (实现子view的绘制,调用drawChild)。
所以我们的onMeasure及后续的方法应该是在onFinishInflate之后调用的。

onSizeChanged

onSizeChanged方法一般是视图大小发生变化的时候回调了,那么具体看源码是在layout的过程中出发的
在layout方法中会调用setFrame方法,在setFrame方法中又调用了sizeChange,在该方法里面回调了onSizeChanged,然后才去回调onLayout过程

public void layout(int l, int t, int r, int b) {  
    if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {  
        onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);  
        mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;  
    }  

    int oldL = mLeft;  
    int oldT = mTop;  
    int oldB = mBottom;  
    int oldR = mRight;  

    boolean changed = isLayoutModeOptical(mParent) ?  
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);  

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {  
        onLayout(changed, l, t, r, b);  

        if (shouldDrawRoundScrollbar()) {  
            if(mRoundScrollbarRenderer == null) {  
                mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);  
            }  
        } else {  
            mRoundScrollbarRenderer = null;  
        }  

        mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;  

        ListenerInfo li = mListenerInfo;  
        if (li != null && li.mOnLayoutChangeListeners != null) {  
            ArrayList listenersCopy =  
                    (ArrayList)li.mOnLayoutChangeListeners.clone();  
            int numListeners = listenersCopy.size();  
            for (int i = 0; i < numListeners; ++i) {  
                listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);  
            }  
        }  
    }  

    mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;  
    mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;  
}  

setFrame过程:

protected boolean setFrame(int left, int top, int right, int bottom) {  
    boolean changed = false;  

    if (DBG) {  
        Log.d("View", this + " View.setFrame(" + left + "," + top + ","  
                + right + "," + bottom + ")");  
    }  

    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {  
        changed = true;  

        // Remember our drawn bit  
        int drawn = mPrivateFlags & PFLAG_DRAWN;  

        int oldWidth = mRight - mLeft;  
        int oldHeight = mBottom - mTop;  
        int newWidth = right - left;  
        int newHeight = bottom - top;  
        boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);  

        // Invalidate our old position  
        invalidate(sizeChanged);  

        mLeft = left;  
        mTop = top;  
        mRight = right;  
        mBottom = bottom;  
        mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);  

        mPrivateFlags |= PFLAG_HAS_BOUNDS;  


        if (sizeChanged) {  
            sizeChange(newWidth, newHeight, oldWidth, oldHeight);  
        }  

        if ((mViewFlags & VISIBILITY_MASK) == VISIBLE || mGhostView != null) {  
            // If we are visible, force the DRAWN bit to on so that  
            // this invalidate will go through (at least to our parent).  
            // This is because someone may have invalidated this view  
            // before this call to setFrame came in, thereby clearing  
            // the DRAWN bit.  
            mPrivateFlags |= PFLAG_DRAWN;  
            invalidate(sizeChanged);  
            // parent display list may need to be recreated based on a change in the bounds  
            // of any child  
            invalidateParentCaches();  
        }  

        // Reset drawn bit to original value (invalidate turns it off)  
        mPrivateFlags |= drawn;  

        mBackgroundSizeChanged = true;  
        if (mForegroundInfo != null) {  
            mForegroundInfo.mBoundsChanged = true;  
        }  

        notifySubtreeAccessibilityStateChangedIfNeeded();  
    }  
    return changed;  
}  

你可能感兴趣的:(Android,自定义view)