Android-View的绘制流程分析

Android-View的绘制流程分析

一、概述

android系统中列表的滑动,动画的展示,文字的渲染等都是android中的View不断的绘制形成的,今天我们就来分析下android的绘制流程

我会从android的源码层面来逐步分析调用过程,关键代码点,以及作用

二、绘制流程图

View.requestLayout()
ViewRootImpl#requestLayout()
ViewRootImpl#scheduleTraversals()
ViewRootImpl#TraversalRunnable
ViewRootImpl#doTraversal()
ViewRootImpl#performTraversals()
ViewRootImpl#performDraw()
ViewRootImpl#draw()
ViewRootImpl#drawSoftware()
View#draw()
View#dispatchDraw(Canvas canvas)
ViewGroup#dispatchDraw(Canvas canvas)
ViewGroup#drawChild
View#draw(canvas, this, drawingTime)
View#draw(Canvas canvas)

三、代码分析

1、View.requestLayout()

这个方法会进行视图的重新测量、布局、绘制

public void requestLayout() {
    
    
    //清理measure的缓存信息,这个是测量的信息
    //这个信息在view的mesure中被创建,measure完成后会被保存下来
    /*
    mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                (long) mMeasuredHeight & 0xffffffffL);
    */
        if (mMeasureCache != null) mMeasureCache.clear();
		/*
		mAttacbhInfo 这个变量在View出现很多,但是感觉我们自己没怎么用过,这个变量不是public的,一般在ViewRootImpl用的多,
		mAttacbhInfo:字面意思是附着的信息,我们知道每个View都是在一个窗口之上,这个里面就有关于窗口的信息
		还有其他的信息比如token,viewrootImpl信息等
		*/
        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
            //这个viewRoot是从mAttachInfo 获取的
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
    
    
                //这个的判断含义是,如果ViewRootImpl正在布局的话,就直接返回了,
				//大家有兴趣点开ViewRootImpl 跟踪下逻辑就知道了
                if (!viewRoot.requestLayoutDuringLayout(this)) {
    
    
                    return;
                }
            }
            //正在布局的view
            mAttachInfo.mViewRequestingLayout = this;
        }
		//添加一个flag标记
    	//这个标记是在layout的标记,在View#layout()中去除
    	//是一个状态标记位
        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;
		//isLayoutRequested() 最终会通过PFLAG_FORCE_LAYOUT来判断,当前是否在layout
    	//如果父视图正在布局中,就返回
        if (mParent != null && !mParent.isLayoutRequested()) {
    
    
            //核心代码,调用父视图的布局,View的父视图 只能是ViewGroup
            //但是ViewGroup 并没有重新requestLayout方法,说明viewgroup还是会调用view的requestlayout
            //这个逻辑就形成了一个层层往上调用的逻辑了
            //当到达最顶层ViewRootImpl的时候mParent==null了,就会调用ViewRootImpl的requestLayout
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
    
    
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

2、ViewRootImpl#requestLayout()

@Override
1046      public void requestLayout() {
    
    
    //没有在布局的话 就进入
1047          if (!mHandlingLayoutInLayoutRequest) {
    
    
    //检查当前操作的线程是否为主线程,这个就是我们常看到的非主线程异常了,这个checkThread 线程检查在
    //这里面很多
    /*
    void checkThread() {
6891          if (mThread != Thread.currentThread()) {
6892              throw new CalledFromWrongThreadException(
6893                      "Only the original thread that created a view hierarchy can touch its views.");
6894          }
6895      }
    */
1048              checkThread();
1049              mLayoutRequested = true;
    //核心代码,进入计划遍历过程中
1050              scheduleTraversals();
1051          }
1052      }

3、ViewRootImpl#scheduleTraversals()

 void scheduleTraversals() {
    
    
1223          if (!mTraversalScheduled) {
    
    
1224              mTraversalScheduled = true;
1225              mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
    /*
    mChoreographer 这个在我的其他文章中有介绍,这个就是android的一个时间编排者,主要是对视图渲染,输入,动画处理做时间的统一,这个的时间是由ServiceFlinger控制的,说白了,由硬件来参与了控制
    发送的Vsync请求信号,然后间隔一定时间收到Vsync信号,这个逻辑由Choreographer做了封装处理
    信号类型CALLBACK_TRAVERSAL
    我们接着看mTraversalRunnable的实现
    */
1226              mChoreographer.postCallback(
1227                      Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
1228              if (!mUnbufferedInputDispatch) {
    
    
1229                  scheduleConsumeBatchedInput();
1230              }
1231              notifyRendererOfFramePending();
1232              pokeDrawLockIfNeeded();
1233          }
1234      }

4、ViewRootImpl#TraversalRunnable

final class TraversalRunnable implements Runnable {
    
    
6336          @Override
6337          public void run() {
    
    
6338              doTraversal();
6339          }
6340      }

5、ViewRootImpl#doTraversal()

void doTraversal() {
    
    
1246          if (mTraversalScheduled) {
    
    
1247              mTraversalScheduled = false;
1248              mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
1249  
1250              if (mProfile) {
    
    
1251                  Debug.startMethodTracing("ViewAncestor");
1252              }
1253  				//核心方法,进入看
1254              performTraversals();
1255  
1256              if (mProfile) {
    
    
1257                  Debug.stopMethodTracing();
1258                  mProfile = false;
1259              }
1260          }
1261      }

6、ViewRootImpl#performTraversals()

//这个方法太长了,无法完全拷贝,我写成了伪代码了
//这个方法是真正的执行分发遍历的核心方法,是ViewRootImpl的核心
//这个方法主要做了三件事
performTraversals()
{
xxxxxx
//执行测量流程
performMeasure()
xxxxx
//执行布局流程
performLayout()
xxx
//执行绘制流程
performDraw()

}
/*
上面的每一个流程最终都会触发View树的对应处理流程
我们先用其中的一个performDraw来分析
*/

7、ViewRootImpl#performDraw()

private void performDraw() {
    
    
2598          if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
    
    
2599              return;
2600          }
2601  
2602          final boolean fullRedrawNeeded = mFullRedrawNeeded;
2603          mFullRedrawNeeded = false;
2604  
2605          mIsDrawing = true;
2606          Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
2607          try {
    
    
    		//核心方法,进入看
2608              draw(fullRedrawNeeded);
2609          } finally {
    
    
2610              mIsDrawing = false;
2611              Trace.traceEnd(Trace.TRACE_TAG_VIEW);
2612          }
2613  
2614          // For whatever reason we didn't create a HardwareRenderer, end any
2615          // hardware animations that are now dangling
2616          if (mAttachInfo.mPendingAnimatingRenderNodes != null) {
    
    
2617              final int count = mAttachInfo.mPendingAnimatingRenderNodes.size();
2618              for (int i = 0; i < count; i++) {
    
    
2619                  mAttachInfo.mPendingAnimatingRenderNodes.get(i).endAllAnimators();
2620              }
2621              mAttachInfo.mPendingAnimatingRenderNodes.clear();
2622          }

8、ViewRootImpl#draw()

public void draw(){
    
    
    xxxx
        .....
        xxxxxx
        省略代码


2810                 if (mAttachInfo.mHardwareRenderer != null &&
2811                          !mAttachInfo.mHardwareRenderer.isEnabled() &&
2812                          mAttachInfo.mHardwareRenderer.isRequested()) {
    
    
2813  
2814                      try {
    
    
2815                          mAttachInfo.mHardwareRenderer.initializeIfNeeded(
2816                                  mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
2817                      } catch (OutOfResourcesException e) {
    
    
2818                          handleOutOfResourcesException(e);
2819                          return;
2820                      }
2821  
2822                      mFullRedrawNeeded = true;
2823                      scheduleTraversals();
2824                      return;
2825                  }
2826  					//这里是核心代码,这里的参数surface对应的表面,dirty是要绘制的区域
2827                  if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
    
    
2828                      return;
2829                  }
2830              }
2831          }
2832  
2833          if (animating) {
    
    
2834              mFullRedrawNeeded = true;
2835              scheduleTraversals();
2836          }
2837      }

9、ViewRootImpl#drawSoftware()

2842      private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
2843              boolean scalingRequired, Rect dirty) {
    
    
2844  
2845          // Draw with software renderer.
2846          final Canvas canvas;
2847          try {
    
    
2848              final int left = dirty.left;
2849              final int top = dirty.top;
2850              final int right = dirty.right;
2851              final int bottom = dirty.bottom;
2852  			  //锁定surface 获得canvas 这个操作大家都熟悉把,这个surface是Ui线程下的一个surface,所有的View共享一个surface,
2853              canvas = mSurface.lockCanvas(dirty);
2854  
2855              // The dirty rectangle can be modified by Surface.lockCanvas()
2856              //noinspection ConstantConditions
2857              if (left != dirty.left || top != dirty.top || right != dirty.right
2858                      || bottom != dirty.bottom) {
    
    
2859                  attachInfo.mIgnoreDirtyState = true;
2860              }
2861  
2862              // TODO: Do this in native
    				//设置密度
2863              canvas.setDensity(mDensity);
2864          } catch (Surface.OutOfResourcesException e) {
    
    
2865              handleOutOfResourcesException(e);
2866              return false;
2867          } catch (IllegalArgumentException e) {
    
    
2868              Log.e(mTag, "Could not lock surface", e);
2869              // Don't assume this is due to out of memory, it could be
2870              // something else, and if it is something else then we could
2871              // kill stuff (or ourself) for no reason.
2872              mLayoutRequested = true;    // ask wm for a new surface next time.
2873              return false;
2874          }
2875  
2876          try {
    
    
2877              if (DEBUG_ORIENTATION || DEBUG_DRAW) {
    
    
2878                  Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
2879                          + canvas.getWidth() + ", h=" + canvas.getHeight());
2880                  //canvas.drawARGB(255, 255, 0, 0);
2881              }
2882  
2883              // If this bitmap's format includes an alpha channel, we
2884              // need to clear it before drawing so that the child will
2885              // properly re-composite its drawing on a transparent
2886              // background. This automatically respects the clip/dirty region
2887              // or
2888              // If we are applying an offset, we need to clear the area
2889              // where the offset doesn't appear to avoid having garbage
2890              // left in the blank areas.
2891              if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
    
    
    //做一些颜色的初始化
2892                  canvas.drawColor(0, PorterDuff.Mode.CLEAR);
2893              }
2894  
2895              dirty.setEmpty();
2896              mIsAnimating = false;
2897              mView.mPrivateFlags |= View.PFLAG_DRAWN;
2898  
2899              if (DEBUG_DRAW) {
    
    
2900                  Context cxt = mView.getContext();
2901                  Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
2902                          ", metrics=" + cxt.getResources().getDisplayMetrics() +
2903                          ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
2904              }
2905              try {
    
    
    //做一些画布的移动
2906                  canvas.translate(-xoff, -yoff);
2907                  if (mTranslator != null) {
    
    
2908                      mTranslator.translateCanvas(canvas);
2909                  }
2910                  canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
2911                  attachInfo.mSetIgnoreDirtyState = false;
2912  				  //这个是核心方法,这个draw是View类的方法
    				  //这个mView就是Dectorview,继承自FrameLayout,FrameLayout继承自ViewGroup
    				  //所以这个draw 我们就先到ViewGroup寻找,发现ViewGroup没有重写,直接用的view						的draw
    				  //去View查看draw,进入看
2913                  mView.draw(canvas);
2914  
2915                  drawAccessibilityFocusedDrawableIfNeeded(canvas);
2916              } finally {
    
    
2917                  if (!attachInfo.mSetIgnoreDirtyState) {
    
    
2918                      // Only clear the flag if it was not set during the mView.draw() call
2919                      attachInfo.mIgnoreDirtyState = false;
2920                  }
2921              }
2922          } finally {
    
    
2923              try {
    
    
    //绘制完成就post,通知绘制
2924                  surface.unlockCanvasAndPost(canvas);
2925              } catch (IllegalArgumentException e) {
    
    
2926                  Log.e(mTag, "Could not unlock surface", e);
2927                  mLayoutRequested = true;    // ask wm for a new surface next time.
2928                  //noinspection ReturnInsideFinallyBlock
2929                  return false;
2930              }
2931  
2932              if (LOCAL_LOGV) {
    
    
2933                  Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
2934              }
2935          }
2936          return true;
2937      }

10、View#draw()

public void draw(Canvas canvas) {
    
    
    final int privateFlags = mPrivateFlags;
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    /*
    我们发现google已经为我们清晰的注释了draw要做的工作了
     * Draw traversal performs several drawing steps which must be executed
     * in the appropriate order:
     *
     *      1. Draw the background  画背景
     *      2. If necessary, save the canvas' layers to prepare for fading 保存layers
     *      3. Draw view's content 画内容
     *      4. Draw children 画子视图,这个是核心,这个会调用ViewGroup中的dispatchDraw
     *      5. If necessary, draw the fading edges and restore layers
     *      6. Draw decorations (scrollbars for instance) //画滑动bar
     *      7. If necessary, draw the default focus highlight
     */

    // Step 1, draw the background, if needed
    int saveCount;

    drawBackground(canvas);

    // skip step 2 & 5 if possible (common case)
    final int viewFlags = mViewFlags;
    boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
    boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
    if (!verticalEdges && !horizontalEdges) {
    
    
        // Step 3, draw the content
        onDraw(canvas);

        // Step 4, draw the children
        //我们继续看View的dispatchDraw方法
        dispatchDraw(canvas);

        drawAutofilledHighlight(canvas);

        // Overlay is part of the content and draws beneath Foreground
        if (mOverlay != null && !mOverlay.isEmpty()) {
    
    
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // Step 6, draw decorations (foreground, scrollbars)
        onDrawForeground(canvas);

        // Step 7, draw the default focus highlight
        drawDefaultFocusHighlight(canvas);

        if (isShowingLayoutBounds()) {
    
    
            debugDrawFocus(canvas);
        }

        // we're done...
        return;
    }

11、View#dispatchDraw(Canvas canvas)

protected void dispatchDraw(Canvas canvas) {
    
    
//空实现,因为view不负责分发,只有去ViewGroup中找了
}

12、ViewGroup#dispatchDraw(Canvas canvas)

protected void dispatchDraw(Canvas canvas) {
    
    
        boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;
        int flags = mGroupFlags;
		//处理布局动画的东西
        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
    
    
            final boolean buildCache = !isHardwareAccelerated();
            for (int i = 0; i < childrenCount; i++) {
    
    
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
    
    
                    final LayoutParams params = child.getLayoutParams();
                    attachLayoutAnimationParameters(child, params, i, childrenCount);
                    bindLayoutAnimation(child);
                }
            }

            final LayoutAnimationController controller = mLayoutAnimationController;
            if (controller.willOverlap()) {
    
    
                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
            }

            controller.start();

            mGroupFlags &= ~FLAG_RUN_ANIMATION;
            mGroupFlags &= ~FLAG_ANIMATION_DONE;

            if (mAnimationListener != null) {
    
    
                //布局动画的开始回调
                mAnimationListener.onAnimationStart(controller.getAnimation());
            }
        }
		/*
		下面的过程主要是对viewgroup中的canvas进行裁切,就是clipRect操作,让对应的View只能在对应的区域绘制,所以先要canvas.save 保存当前的状态,后面来恢复,
		我们看到里面有padding,这个padding是只有ViewGroup有的,裁切的返回会减去这个边距
		*/
        int clipSaveCount = 0;
        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
        if (clipToPadding) {
    
    
            clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
                    mScrollX + mRight - mLeft - mPaddingRight,
                    mScrollY + mBottom - mTop - mPaddingBottom);
        }
		//下面就是开始正式的遍历子视图,然后遍历绘制
        // We will draw our child's animation, let's reset the flag
       //清除标志
        mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;

        boolean more = false;
        final long drawingTime = getDrawingTime();

        if (usingRenderNodeProperties) canvas.insertReorderBarrier();
        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
        int transientIndex = transientCount != 0 ? 0 : -1;
        // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
        // draw reordering internally
        final ArrayList<View> preorderedList = usingRenderNodeProperties
                ? null : buildOrderedChildList();
        final boolean customOrder = preorderedList == null
                && isChildrenDrawingOrderEnabled();
    /*
    这个方法的核心就是对子view的遍历然后绘制分发
    */
        for (int i = 0; i < childrenCount; i++) {
    
    
            while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
    
    
                final View transientChild = mTransientViews.get(transientIndex);
                if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                        transientChild.getAnimation() != null) {
    
    
                    //调用drawChild 绘制
                    more |= drawChild(canvas, transientChild, drawingTime);
                }
                transientIndex++;
                if (transientIndex >= transientCount) {
    
    
                    transientIndex = -1;
                }
            }

            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
    
    
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        while (transientIndex >= 0) {
    
    
            // there may be additional transient views after the normal views
            final View transientChild = mTransientViews.get(transientIndex);
            if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
                    transientChild.getAnimation() != null) {
    
    
                more |= drawChild(canvas, transientChild, drawingTime);
            }
            transientIndex++;
            if (transientIndex >= transientCount) {
    
    
                break;
            }
        }
        if (preorderedList != null) preorderedList.clear();

        // Draw any disappearing views that have animations
    //这个mDisappearingChildren 是只的是 正在因为执行动画 即将消失的View,这些View执行了去除动画,所以view已经移除。不能点击,但是还是要把他们绘制出来的
        if (mDisappearingChildren != null) {
    
    
            final ArrayList<View> disappearingChildren = mDisappearingChildren;
            final int disappearingCount = disappearingChildren.size() - 1;
            // Go backwards -- we may delete as animations finish
            for (int i = disappearingCount; i >= 0; i--) {
    
    
                final View child = disappearingChildren.get(i);
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        if (usingRenderNodeProperties) canvas.insertInorderBarrier();

        if (isShowingLayoutBounds()) {
    
    
            onDebugDraw(canvas);
        }

        if (clipToPadding) {
    
    
            canvas.restoreToCount(clipSaveCount);
        }

        // mGroupFlags might have been updated by drawChild()
        flags = mGroupFlags;
		//因为设置了FLAG_INVALIDATE_REQUIRED 可能重绘
        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
    
    
            invalidate(true);
        }

        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
                mLayoutAnimationController.isDone() && !more) {
    
    
            // We want to erase the drawing cache and notify the listener after the
            // next frame is drawn because one extra invalidate() is caused by
            // drawChild() after the animation is over
            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
            final Runnable end = new Runnable() {
    
    
               @Override
               public void run() {
    
    
                   notifyAnimationListener();
               }
            };
            //布局动画完成了就回调
            post(end);
        }
    }

13、ViewGroup#drawChild

protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    
    
    //调用了View的draw方法,进度看
    return child.draw(canvas, this, drawingTime);
}

14、View#draw(canvas, this, drawingTime)

//这个方法是View和View的通用绘制处理
//主要是关于Canvas 的裁剪,移动,动画处理,分发
protected void draw(canvas, this, drawingTime)
{
    
    
	xxxxx
        省略代码
        ...
        //关于动画的处理
         more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
    
    
    。。。。
        
        
         int sx = 0;
        int sy = 0;
        if (!drawingWithRenderNode) {
    
    
            //看到这个大家熟悉了吗,这个就是那个处理scroller 需要的回调
            computeScroll();
            sx = mScrollX;
            sy = mScrollY;
        }
    //一堆关于canvas的处理,比如offsetForScroll。canvas.translate,还有Transformation 动画
    if (drawingWithRenderNode) {
    
    
                            renderNode.setAnimationMatrix(transformToApply.getMatrix());
                        } else {
    
    
                            // Undo the scroll translation, apply the transformation matrix,
                            // then redo the scroll translate to get the correct result.
                            canvas.translate(-transX, -transY);
        //实现动画的移动
                            canvas.concat(transformToApply.getMatrix());
                            canvas.translate(transX, transY);
                        }
    
     // Deal with alpha if it is or used to be <1
    //下面代码是处理alpha 透明度的绘制,有些View是设置了alpha的
            if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
    
    
                if (alpha < 1) {
    
    
                    mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA;
                } else {
    
    
                    mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_ALPHA;
                }
                parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
                if (!drawingWithDrawingCache) {
    
    
                    final int multipliedAlpha = (int) (255 * alpha);
                    if (!onSetAlpha(multipliedAlpha)) {
    
    
                        if (drawingWithRenderNode) {
    
    
                            renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
                        } else if (layerType == LAYER_TYPE_NONE) {
    
    
                            canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),
                                    multipliedAlpha);
                        }
                    } else {
    
    
                        // Alpha is handled by the child directly, clobber the layer's alpha
                        mPrivateFlags |= PFLAG_ALPHA_SET;
                    }
                }
            }
        } else if ((mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
    
    
            onSetAlpha(255);
            mPrivateFlags &= ~PFLAG_ALPHA_SET;
        }
    




if (!drawingWithDrawingCache) {
    
    
            if (drawingWithRenderNode) {
    
    
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                ((RecordingCanvas) canvas).drawRenderNode(renderNode);
            } else {
    
    
                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
    
    
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    //核心代码,会调用ViewGroup中的dispathchDraw 然会形成递归调用,直到最后一个是View为止
                    dispatchDraw(canvas);
                } else {
    
    
                    //调用自己的View的draw
                    draw(canvas);
                }
            }
        }



    
        
        ...
        省略代码
        xxxxxx
}

15、View#draw(Canvas canvas)

之前那个View#draw,实际是Viewgroup,会调用dispatchDraw

但是现在这个是一个View 调用自己的dispatchDraw 是空实现

到此为止所有的遍历走完了

之前说的measure ,layout跟这个draw的调用类似,自行分析

四、总结

整个过程包括通知和执行

通知布局实质就是调用View的跟节RootViewImpl,

执行:测量、布局、绘制,核心是通过不断的ViewGroup的遍历,迭代,直到最后的View是一个View,为止

我们可以把这个这个View形象的比喻成一棵树,整个就是View树,根部是RootViewImpl,RootViewImpl上面是DectorView,然后在上面就是由各种ViewGroup和View组成的视图,最上面的叶子是View,比如TextView

猜你喜欢

转载自blog.csdn.net/fagawee/article/details/121084883