自定义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; }
- 自定义view中onSizeChanged、onFinishInflate方法调用时机
- 自定义view中onMeasure、onLayout、onDraw、onFinishInflate、onSizeChanged方法调用时机
- 自定义组件-onFinishInflate&onSizeChanged
- 自定义View 中一些方法的调用时机
- 在自定义View中onFinishInflate作用
- android,view的执行过程onDraw、onSizeChanged,onFinishInflate
- android,view的执行过程 onDraw、onSizeChanged,onFinishInflate
- Android View的onFinishInflate和onSizeChanged生命周期详解
- Android View的onFinishInflate和onSizeChanged生命周期详解
- android自定义组合控件onFinishInflate和onSizeChanged的区别
- onFinishInflate onSizeChanged onDraw 运行顺序
- Android 自定义ViewGroup中onFinishInflate方法可以用来干什么
- Android游戏开发之旅View类详解自定义View的常用方法:onFinishInflate
- view的onFinishInflate()何时调用的?
- view的onFinishInflate()何时调用的?
- view的onFinishInflate()何时调用的?
- view的onFinishInflate()何时调用的?
- view的onFinishInflate()何时调用的?
- 输入一行字符,计算字符中有多少单词,单词和单词之间以空格分开
- web项目导入eclipse为何显示java项目
- 8.4 Calendar类
- Spring高级应用之注入各类集合
- C# 网站 获取客户端IP地址详细信息
- 自定义view中onSizeChanged、onFinishInflate方法调用时机
- bootstrap table初始化参数
- 深度学习框架哪家强?MXNet称霸CNN、RNN和情感分析,TensorFlow仅擅长推断特征提取
- AI一分钟 | 大福利!谷歌2018年将公开内部机器学习培训课程;医疗影像领域今年最大融资出炉!图玛深维获软银中国2亿人民币投资
- 干货 | 成为一名推荐系统工程师永远都不晚
- springMVC引入swagger2以后,Junit测试无法通过
- 2PC和3PC中故障情况分析
- SpringMVC的原理
- 百万富翁与陌生人的交换