美文网首页view绘制
View的绘制流程 - onLayout()源码分析

View的绘制流程 - onLayout()源码分析

作者: 世道无情 | 来源:发表于2018-03-27 08:26 被阅读3次

    前言

    View绘制流程系列文章
    View的绘制流程 - onMeasure()源码分析
    View的绘制流程 - onLayout()源码分析
    View的绘制流程 - onDraw()源码分析

    结论


    View的绘制流程都是从ViewRootImpl中的requestLayout()方法开始进去的,performMeasure()、performLayout()、performDraw(),而如果代码中又写了这样的代码:addView()、setVisibility()等方法,意思就是会重新执行requestLayout(),意思就是会重新执行View的绘制流程,这个时候执行View的绘制流程时不会和第一次一样去执行所有的逻辑,比如说你自己addView(),一次性添加了10个View,那么它有可能等你添加完毕之后才去执行 View的绘制流程的:

    performLayout()调用方法顺序如下:
    在 ViewRootImpl中的 performLayout()方法 ->
    然后调用View中的 layout()方法 ->
    然后调用View中的 onLayout()方法 ,这里还是和上节课讲解 onMeasure()源码分析举的例子一样,还是用LinearLayout垂直的布局包裹3个 TextView ->
    摆放子布局, for循环所有子View,前提是子View不为 GONE,然后调用 child.layout()方法

    下边进行分析,最下边的结论可以不看,因为和上边这个一样,下边仅用于分析流程。

    1. 说明


    上节课我们看了下View绘制流程中的 onMeasure()方法,那么这节课我们就来看下onLayout()方法
    View的绘制流程的入口就是 ViewRootImpl中的 requestLayout()方法,源码如下,从requestLayout()中点击进入View的 layout()方法:

    ViewRootImpl源码如下:

        /**
         * We have one child
         */
        public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;
    
                    mAttachInfo.mDisplayState = mDisplay.getState();
                    mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
    
                    // Schedule the first layout -before- adding to the window
                    // manager, to make sure we do the relayout before receiving
                    // any other events from the system.
                    requestLayout();
                }
            }
        }
    
    
    @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                checkThread();
                mLayoutRequested = true;
                scheduleTraversals();
            }
        }
    
    void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    
    final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                doTraversal();
            }
        }
    
    void doTraversal() {
            if (mTraversalScheduled) {
                mTraversalScheduled = false;
                mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
                performTraversals();
            }
        }
    
    private void performTraversals() {
            // cache mView since it is used so much below...
            final View host = mView;
    
            if (DBG) {
                System.out.println("======================================");
                System.out.println("performTraversals");
                host.debug();
            }
    
                        if (measureAgain) {
                            if (DEBUG_LAYOUT) Log.v(mTag,
                                    "And hey let's measure once more: width=" + width
                                    + " height=" + height);
                            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                        }
                }
            } else 
            if (didLayout) {
                performLayout(lp, mWidth, mHeight);
                }
    
                performDraw();
            } else {
                
            }
    
            mIsInTraversal = false;
        }
    
    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
              int desiredWindowHeight) {
          final View host = mView;
          if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
              Log.v(mTag, "Laying out " + host + " to (" +
                      host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
          }
    
          Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
          try {
              host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    
              mInLayout = false;
              int numViewsRequestingLayout = mLayoutRequesters.size();
              if (numViewsRequestingLayout > 0) {
                          false);
                  if (validLayoutRequesters != null) {
                      // Process fresh layout requests, then measure and layout
                      int numValidRequests = validLayoutRequesters.size();
                      for (int i = 0; i < numValidRequests; ++i) {
                          final View view = validLayoutRequesters.get(i);
                          Log.w("View", "requestLayout() improperly called by " + view +
                                  " during layout: running second layout pass");
                          view.requestLayout();
                      }
                      measureHierarchy(host, lp, mView.getContext().getResources(),
                              desiredWindowWidth, desiredWindowHeight);
                      mInLayout = true;
                      host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight())
                      if (validLayoutRequesters != null) {
                          final ArrayList<View> finalRequesters = validLayoutRequesters;
                          // Post second-pass requests to the next frame
                          getRunQueue().post(new Runnable() {
                              @Override
                              public void run() {
                                  int numValidRequests = finalRequesters.size();
                                  for (int i = 0; i < numValidRequests; ++i) {
                                      final View view = finalRequesters.get(i);
                                      Log.w("View", "requestLayout() improperly called by " + view +
                                              " during second layout pass: posting in next frame");
                                      view.requestLayout();
                                  }
                              }
                          });
                      }
      }
    
     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);
        }
    

    performLayout()调用方法顺序如下:
    在 ViewRootImpl中的 performLayout()方法 ->
    然后调用View中的 layout()方法 ->
    然后调用View中的 onLayout()方法 ,这里还是和上节课讲解 onMeasure()源码分析举的例子一样,还是用LinearLayout垂直的布局包裹3个 TextView ->
    摆放子布局, for循环所有子View,前提是子View不为 GONE,然后调用 child.layout()方法

    相关文章

      网友评论

        本文标题:View的绘制流程 - onLayout()源码分析

        本文链接:https://www.haomeiwen.com/subject/dtrqcftx.html