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

来源:互联网 发布:caroemerald 知乎 编辑:程序博客网 时间:2024/06/08 11:26

转自: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("<merge /> 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("<include /> cannot be the root element");            }            parseInclude(parser, context, parent, attrs);        } else if (TAG_MERGE.equals(name)) {            throw new InflateException("<merge /> 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<OnLayoutChangeListener> listenersCopy =                      (ArrayList<OnLayoutChangeListener>)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;  }  
阅读全文
0 0
原创粉丝点击