一般自定义view或viewGroup基本上都会去实现onMeasure、onLayout、onDraw方法,还有另外两个方法是onFinishInflate和onSizeChanged。
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 xmlfinal 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 suppliedparams = 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方法一般是视图大小发生变化的时候回调了,那么具体看源码是在layout的过程中出发的,在layout方法中会调用setFrame方法,在setFrame方法中又调用了sizeChange,在该方法里面回调了onSizeChanged,然后才回去回调onLayout过程。

layout过程:

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 bitint 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 positioninvalidate(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 childinvalidateParentCaches();}// Reset drawn bit to original value (invalidate turns it off)mPrivateFlags |= drawn;mBackgroundSizeChanged = true;if (mForegroundInfo != null) {mForegroundInfo.mBoundsChanged = true;}notifySubtreeAccessibilityStateChangedIfNeeded();}return changed;}

自定义view中onMeasure、onLayout、onDraw、onFinishInflate、onSizeChanged方法调用时机相关推荐

  1. android resolvesize方法,Android 自定义View中onMeasure()中使用resolveSize()是什么意思?

    缥缈止盈 public static int resolveSize(int size, int measureSpec) {         int result = size;         i ...

  2. Android中自定义view的onMeasure()方法详谈

    背景 理解MeasureSpec MeasureSpec 情况分析 结合图例分析 总结 A little bit of progress every day!Come on! 背景 首先关于自定义vi ...

  3. Andoid自定义View的OnMeasure详解和自定义属性

    一,OnMeasure详解 Android开发中偶尔会用到自定义View,一般情况下,自定义View都需要继承View类的onMeasure方法,那么,为什么要继承onMeasure()函数呢?什么情 ...

  4. 安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例

    安卓自定义view中 绘画基本图形点线面,矩形,方形,圆,扇形,文字及沿着特定方向布局,自定义圆角ImageView图片等等相关api使用方法及举例,图片压缩处理逻辑 本文旨在介绍自定义View的实现 ...

  5. 【Android】自定义view之onMeasure

    1 onMeasure什么时候会被调用 onMeasure方法的作用是测量控件的大小,当我们创建一个View(执行构造方法)的时候不需要测量控件的大小,只有将这个view放入一个容器(父控件)中的时候 ...

  6. Android横竖屏切换View设置不同尺寸或等比例缩放的自定义View的onMeasure解决方案(2)...

    Android横竖屏切换View设置不同尺寸或等比例缩放的自定义View的onMeasure解决方案(2) 附录文章1以xml布局文件方式实现了一个view在横竖屏切换时候的大小尺寸缩放,实现这种需求 ...

  7. 关于自定义View中wrap_content属性失效的问题

    我们在使用自定义控件的时候,有时候会发现当我们设置子View的属性为wrap_content时,发现它最终展现的效果跟我们说预想的不一样,它展现的是match_parent的效果,这是为什么呢?先把问 ...

  8. 自定义View之onMeasure()

    1.自定义View之onMeasure() 2.onMeasure实例分析

  9. Android 自定义View中坐标点的理解学习(一)

    本文主要是记录学习自定义view中看到的资料,为了方便记忆做了保存整理便于自己学习也方便其他Android开发爱好者学习,参考资料看底部链接. 一.getLocationInWindow和getLoc ...

最新文章

  1. WPF与缓动(一) N次缓动
  2. 高等应用数学问题的matlab求解汇总
  3. Apache ZooKeeper - ZK的基本特性与节点应用场景一览
  4. Connection to node 0 (/192.168.204.131:9092) could not be established
  5. 方舟非主机服务器无限距离,方舟非专业服务器距离限制怎么解除 | 手游网游页游攻略大全...
  6. 【牛客 - 330C】Applese 走迷宫(bfs)
  7. SkipList 以及高度的确定
  8. 生命大发的意义 BY 几米
  9. IoT -- (四) 物联网系统架构介绍
  10. python爬虫获取标签规则_Python爬虫之数据提取-selenium定位获取标签对象并提取数据...
  11. ODrive踩坑(二)3508电机和TLE5012B磁编码器参数配置、校准、位置闭环模式转动电机(TLE5012B-E1000)
  12. C语言之volatile用法(二十一)
  13. cuda对应pytorh安装
  14. 4.正则匹配与re模块
  15. wince 百度地图懒人包_百度导航车载wince版|百度地图winCE版 V10.9.2 安卓版 下载_当下软件园_软件下载...
  16. 企业邮箱邮件的服务器地址是什么?企业邮箱服务器出错怎么办?
  17. QQ空间批量删除说说
  18. 网络协议学习笔记 · 22
  19. [Mac]开启鼠标键
  20. 官方发布!2021下半年二建考试报名注意事项!

热门文章

  1. 面向对象与面向过程的区别
  2. 用vue实现一个根据不同百分比显示不同背景颜色的日历
  3. 会议室预约系统遇到的问题
  4. 微信小程序实现俄罗斯方块
  5. 如何在ArcGIS中调用星图地球数据云的数据?
  6. lyrebird(MOCK平台)美团琴鸟,windows系统环境搭建以及使用手册
  7. 施密特正交 matlab,matlab-线性代数 施密特正交化
  8. 51单片机密码锁设计
  9. SQLYong设置Tab缩进
  10. “三项能力超过ChatGPT”,科大讯飞星火大模型现场接受观众挑战,写稿制表PPT通通拿下...