Android View requestLayout 与 onDraw跟onMeasure的关系(一)
2017-09-21 11:53
846 查看
requestLayout
如果一个View的大小、位置等View的形状发生变化后,我们可以调用这个函数请求重新布局,将变化后的View更新到屏幕上。如果子View调用了该方法,那么会从View树重新进行一次测量、布局、绘制这三个流程。简介
调用requestLayout申请重新布局后一般是会调用该View的onDraw()跟ViewGroup的dispatchDraw()只有在如下几种情况下不调用绘制draw函数:
1、View 不用显示的时候,包含两种情况:
view的Visibility属性不为View.VISIBLE
view设置了OnPreDrawListener,并在onPreDraw里返回了false
2、app还没启动完成正在创建Surface的时候,即app显示的Surface还没有创建完成之前调用了
3、Window窗口正在跑动画的时候不会调用,但是requestLayout的请求在窗口动画第一帧之前会调用一次,即窗口上没有任何显示内容的时候会调用一次将内容更新到窗口上。
调用requestLayout申请重新布局后,肯定是会调用该View的onMeasure方法,以及该View的所有父View的onMeasure方法。对于该View的子View,如果布局发生变化则也会调用onMeasure方法,否则是不会调的,所以你调用了父View的requestLayout方法,如果子View的布局没有发生变化,是不会调用它的onMeasure方法的。
源码分析
源码本版:android-7.0.0_r1首先查看frameworks/base/core/java/android/view/View.java 文件里的requestLayout()函数
public void requestLayout() { if (mMeasureCache != null) mMeasureCache.clear(); if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { //如果当前View在请求布局的时候,View树正在进行布局流程的话 //该请求会延迟到布局流程完成后或者绘制流程完成且下一次布局发现的时候再执行 if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; } //设置该View的标志位flag mPrivateFlags |= PFLAG_FORCE_LAYOUT; mPrivateFlags |= PFLAG_INVALIDATED; if (mParent != null && !mParent.isLayoutRequested()) { //将布局的请求递交父View //就这样一级一级的向上传递,最终传递到ViewRootImpl mParent.requestLayout(); } if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) { mAttachInfo.mViewRequestingLayout = null; } }
代码中可以看出,这里主要做了三件事情:一判断View树是否正处于布局流程,是则暂缓处理请求,二设置该View的标志位,三将请求递交给父view.
所以重新布局的请求会从下面一级一级往上传递,最终到达ViewRootImpl来处理
查看frameworks/base/core/java/android/view/ViewRootImpl.java文件
public void requestLayout() { //假如当前正在处理布局变动,则不处理这次的请求 if (!mHandlingLayoutInLayoutRequest) { //检查是否是Main线程调用的 checkThread(); //设置重新布局的请求为true,处理过这个请求后把它置false mLayoutRequested = true; //开启处理任务 scheduleTraversals(); } }
void scheduleTraversals() { //已经开启任务则不再开启 if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //异步调用真正的工作函数mTraversalRunnable mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
final class TraversalRunnable implements Runnable { @Override public void run() { //处理布局请求的函数 doTraversal(); } }
void doTraversal() { //mTraversalScheduled前面设置的true if (mTraversalScheduled) { mTraversalScheduled = false; //任务移出队列 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } //处理任务 performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
从上面代码可以看出,重新布局的请求最终会在一个异步队列里处理,performTraversals函数过长,下面就只列出关键部位代码
//没有处于停止状态,或者已经上报了强制绘制的属性 if (!mStopped || mReportNextDraw) { boolean focusChangedDueToTouchMode = ensureTouchModeLocally( (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); //各种布局发生变化的状态判断 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() || framesChanged || updatedConfiguration) { int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth=" + mWidth + " measuredWidth=" + host.getMeasuredWidth() + " mHeight=" + mHeight + " measuredHeight=" + host.getMeasuredHeight() + " framesChanged=" + framesChanged); // Ask host how big it wants to be //调度Measure performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // Implementation of weights from WindowManager.LayoutParams // We just grow the dimensions as needed and re-measure if // needs be int width = host.getMeasuredWidth(); int height = host.getMeasuredHeight(); boolean measureAgain = false; if (lp.horizontalWeight > 0.0f) { width += (int) ((mWidth - width) * lp.horizontalWeight); childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); measureAgain = true; } if (lp.verticalWeight > 0.0f) { height += (int) ((mHeight - height) * lp.verticalWeight); childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); measureAgain = true; } if (measureAgain) { if (DEBUG_LAYOUT) Log.v(mTag, "And hey let's measure once more: width=" + width + " height=" + height); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); } //设置重新布局的请求为true //在判断performLayout的时候会用到 layoutRequested = true; } } } else {
//是否需要处理布局 final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); boolean triggerGlobalLayoutListener = didLayout || mAttachInfo.mRecomputeGlobalAttributes; //如果处理了performMeasure,就会处理performLayout if (didLayout) { performLayout(lp, mWidth, mHeight);
//View是否不需要绘制,两种情况下不需要绘制 //view设置了OnPreDrawListener并在onPreDraw里返回了false,或者view的Visibility属性不为View.VISIBLE boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible; //View不需要绘制跟Surface没有创建的时候,不需要调度绘制函数 if (!cancelDraw && !newSurface) { if (mPendingTransitions != null && mPendingTransitions.size() > 0) { for (int i = 0; i < mPendingTransitions.size(); ++i) { mPendingTransitions.get(i).startChangingAnimations(); } mPendingTransitions.clear(); } performDraw(); } else {
而ViewRootImpl最终通过performMeasure,performLayout,performDraw来调用View的mersure,layout,draw函数来将这些事件在一级一级的分发给子View,直到调用的View
相关文章推荐
- (总结篇)Android 牛不牛?决定于自定义View控件(一)——view绘制流程(onMeasure,onLayout,onDraw)
- Android自定义ViewGroup:onMeasure与onLayout(1)
- Android之OnMeasure、OnLayout、OnDraw
- Android TextView重写onMeasure和onDraw显示无拉伸图片(含实现代码链接)
- view的onMeasure,onLayout,onDraw源码分析
- Android绘制的一些总结onMeasure,onLayout,onDraw
- invalidate() ondraw() onmeasure() onlayout() requestLayout() requestFocus()
- 自定义View中的onMeasure,onLayout,onDraw
- 安卓开发中自定义View之onMeasure(),onLayout(),onDraw()讲解(一)
- invalidate() ondraw() onmeasure() onlayout() requestLayout() requestFocus()
- Android 自定义View基础 onMeasure & onLayout
- view的onMeasure,onLayout,onDraw源码分析(下)
- 自定义View中的onMeasure,onLayout,onDraw
- 自定义View之onMeasure(),onLayout(),onDraw()
- Android自定义ViewGroup:onMeasure与onLayout(1)
- 关于View的onMeasure()、onSizeChanged()、onLayout()、onDraw()调用顺序
- 自定义View中的方法onMeasure,onLayout,onDraw作用
- 关于View的onMeasure()、onSizeChanged()、onLayout()、onDraw()调用顺序
- 探讨Android ViewGroup(Layout)和View中onInterceptTouchEvent和onTouchEvent调用关系详解
- Android 自定义View实现圆形进度条 深入理解onDraw和onMeasure及自定义属性