正文分析的源码基于 Android API 25,View的两个draw方法是有分歧等的调用顺序的


博文出处:View的做事原理,欢迎大家关怀自己的博客,谢谢!

ViewRootImpl充当的是View和window之间的纽带。在startActivity之后,经过与ActivityManagerService(Service)的IPC交互,会在ActivityThread的handleResumeActivity方法中实施到getWindow().addView,就是将根布局
Decor添加到window中以展示。getWindow会以WindowManagerGloble来实施addView方法,其中就会成立ViewRootImpl实例并调用其setView方法传入Decor布局,在setView中会执行到performTranvesals方法,这些艺术是重中之重:
<pre>
private void performTraversals() {
……
performMeasure();
……
performLayout();
……
performDraw();
……
}
</pre>
会挨个执行Measure测量、Layout布局和Draw绘制。
随后来分析performDraw的履行路径:
ViewRootImpl.performDraw->ViewRootImpl.draw->ThreadedRenderer.draw->ThreadedRenderer.updateRootDisplayList->ThreadedRenderer.updateViewTreeDIsplayList->View.updateDisplayListIfDirty->PhoneWindow$DecorView.draw->View.draw->ViewGroup.dispatchDraw->ViewGroup.drawChild->View.draw
率先个跳跃的类是ThreadedRenderer,看名字就可以猜出类是用以绘制线程的,在ViewRootImpl.setView方法中会调用enableHardwareAcceleration(attars)方法:
<pre>
public void setView(View view, WindowManager.LayoutParams attars, View
panelParnetView) {
……
if (mSUrfaceHolder == null) {
enableHardwareAcceleration(attrs);
}
……
}

阅读者三篇Android绘制小说,会让你对通晓Android绘制有扶持:

注:本文分析的源码基于 Android API 25

private void enableHardwareAcceleration(WindowManager.Layoutparams
attars) {
……
mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext,
translucent);
……
}
</pre>
最终调用create方法成立HardwareRenderer实例:
<pre>
static HardwareRenderer create(Context context, boolean translucent) {
HardwareRenderer renderer = null;
if (DisplayListCanvas.isAvailable()) {
renderer = new ThreadedRenderer(context, translucent);
}
return renderer;
}
</pre>
那边创办的是ThreadedRenderer(继承自HardwareRenderer),然后调用ThreadedRenderer的draw方法,再调其updateRootDisplayList方法,再调其updateViewTreeDisplayList方法,末了就调用其参数view(也就是DecorView)的updateDisplayListIfDirty方法,在这一个办法里会调用View的draw(canvas)绘制方法,由于DecorView方法重写了draw方法,所以先进行DecorView的draw方法:
<pre>
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenubackground.draw(canvas);
}
}
</pre>
方法开端就直接调用了父类(View)的draw(canvas)方法(那里要求证实,View有四个draw重载方法,所以上边提到draw方法都会带上分歧的参数来区分):
<pre>
/**
*动用所给的Canvas手动刷新view(和她所有的childrend)。
*在刷新在此以前,view必须做了整机的测量布局。创立view的时候
*设若须求自定义绘制就重写onDraw方法,否则直接调用super即可。
public void draw(Canvas canvas) {
……
//步骤1,绘制背景
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
//一般境况下会跳过步骤2和5
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
//步骤3,绘制内容
if (!dirtyOpaque) onDraw(canvas);
//步骤4,绘制children
dispatchDraw(canvas);
//浮层是内容的一有些并绘制在前景之下
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
//步骤6,绘制装饰(前景,进程条)
onDrawForeground(canvas);
//自定义操作
reutrn;
}

View绘制的源点

boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;

float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;

//步骤2,保存canvas图层
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
    paddingLeft += getLeftPaddingOffset();
}

int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
    right += getRightpaddingOffset();
    bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mSrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
//如果顶部和底部的渐变层重叠导致很奇怪的显示,就去剪切
if (verticalEdges && (top + length > bottom - length)) {
    length = (bottom - top) / 2;
 }
 //如果有需要的话,横向渐变也剪切
 if (horizontalEdges && (left + length > right - length)) {
    length = (right -left) / 2;
 }
if (verticalEdges) {
    topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
    drawTop = topFadeStrength * fadeHeight > 1.0f;
    bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
    drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
if (horizontalEdges) {
    leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
    drawLeft = leftFadeStrength * fadeHeight > 1.0f;
    rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
    drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolideColor();
if (solidColor == 0) {
    final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
    if (drawTop) {
        canvas.saveLayer(left, top, right, top+length, null, flags);
    }
    if (drawBottom) {
        canvas.saveLayer(left, bottom-length, right, bottom, null, flags);
    }
    if (drawLeft) {
        canvas.saveLayer(left, top, left+length, bottom, null, flags);
    }
    if (drawRight) {
        canvas.saveLayer(right-length, top, right, bottom, null, flags);
    }
} else {
    scrollabilityCache.setFadeColor(solidColor);
}
//步骤3,绘制内容
if (!dirtyOpaque) onDraw(canvas);
//步骤4, 绘制children
dispatchDraw(canvas);
//步骤5, 绘制渐变效果和重载图层
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
    matrix.setScale(1, fadeHeight * topFadeStrength);
    matrix.postTranslate(left, top);
    fade.setLocalMatrix(matrix);
    p.setShader(fade);
    canvas.drawRect(left, top, right, top+length, p);
}
if (drawBottom) {
    matrix.setScale(1, fadeHeight * bottomFadeStrength);
    matrix.postRotate(180);
    matrix.postTranslate(left, bottom);
    fade.setLocalMatrix(matrix);
    p.setShader(fade);
    canvas.drawRect(left, bottom-length, right, bottom, p);
}
if (drawLeft) {
    matrix.setScale(1, fadeHeight * rightFadeStrength);
    matrix.postRotate(90);
    matrix.postTranslate(right, top);
    fade.setLocalMatrix(matrix);
    p.setShader(fade);
    canvas.drawRect(right-length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
//覆盖层是内容的一部分并且绘制在前景的下面
if (mOverlay != null && !mOverlay.isEmpty()) {
    mOverlay.getOverlayView().dispatchDraw(canvas);
}
//步骤6, 绘制装饰(前景,进度条)
onDrawForeground(canvas);

WindowManagerGlobal

}
</pre>
android源码已经对draw方法举行了验证:一般的绘图步骤:1.制图背景
2.万一急需,保存canvas来为绘制渐变做准备 3.制图view 的始末
4.绘制子childrend 5.如若须要,绘制边界渐变和重载图层
6.绘制装饰层(例如进程条)。
其间第3步和第4步分别调用了onDraw和dispatchDraw,onDraw也就是大家自定义View时重写的绘图接口;dispatchDraw在View中是一个空完成的点子,要求View的子类去落到实处;
<pre>
protected void dispatchDraw(Canvas canvas) {
……
more |= drawChild(canvas, child, drawingTime);
……
}
</pre>
此处调用了ViewGroup的drawChild方法:
<pre>
protected boolean drawChild(Canvas canvas, View child, long drawingTime)
{
return child.draw(canvas, this, drawingTime);
}
</pre>

解析从draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long
drawing提姆e)多个主意出手:

addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)

WindowManagerGlobal
addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)
方法中,创建了 ViewRootImpl 对象,将 ViewRootImplDecorView
相关联:

    root = new ViewRootImpl(view.getContext(), display);
    ...
    // view 是 PhoneWindow 的 DecorView
    root.setView(view, wparams, panelParentView);

成立好了 root 之后,调用了 ViewRootImpl
setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
方法。

将 DecorView 和 ViewRootImpl 相关联。

当然这里之后就已经不是DecorView的代码了,而是她的child
View的主意,但是在调节时系统也把子view的draw(canvas, this,
drawing提姆e)方法纳入第一调用层级,所以那里一起说一下:
<pre>
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
……
if (drawingWithRenderNode) {
renderNode = updateDisplayListIfDirty();
if (!renderNode.isValid()) {
renderNode = null;
drawingWIthRenderNode = false;
}
}
……
}
</pre>
在此处调用了View的updateDisplayListIfDirty方法,那样就从头了新的一轮轮回:view.updateDisplayListIfDirty–>ViewGroup.dispatchDraw–>ViewGroup.drawChild–>新子View.draw(canvas,
parent, drawing提姆(Tim)e)———–>>>>
诸如此类循环往复,直到抵达最终一个子View,在终极一个View的循环中,view.updateDisplayListIfDirty方法里有一段代码要留心:
<pre>
public RenderNode updateDisplayListIfDirty() {
……
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
dispatchDraw(canvas);
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().draw(canvas);
}
} else {
draw(canvas);
}
……
}
</pre>
此间有个if判断,如若建立则调用View.dispatchDraw方法,否则调用View.draw(canvas)方法,这一个就是得了轮回的节点。到达最后一个子View时判断就不树立,直接调用draw(canvas),而在draw方法中(看上边代码段),就会相继调用onDraw和dispatchDraw,此时View的dispatchDraw为空完毕,代码不轮回,直接运行并回到,从而甘休draw轮回。

draw(Canvas canvas)和draw(Canvas canvas, ViewGroup parent, long
drawing提姆(Tim)e)方法从外表看来就是接受的参数分裂,
其实二期有一个先后顺序,从属关系,可是也要分情形,就看脚下的View是还是不是一品的DecorView了,就是说一个View有没有parent
view,View的多少个draw方法是有分化的调用顺序的,当然唯有DecorView是第一级的View,DecorView没有parent
view。

ViewRootImpl

总计一下:整个流程由DecorView初步,到最后子View甘休。DecorView的draw(canvas)中相继执行onDraw和dispatchDraw,此时dispatchDraw有children可以分发,初步轮回…………….到最后一个子View时,其updateDisplayListIfDirty方法通过if判断不执行dispatchDraw而是执行draw(canvas),在她的draw里dispatchDraw没有children可用,所以持续运行并再次来到,甘休轮回。

好端端情状下,draw(Canvas canvas, ViewGroup parent, long
drawing提姆(Tim)e)方法被当下view的parentView调用,在draw(Canvas canvas,
ViewGroup parent, long
drawing提姆e)方法中会依照是不是协理硬件加速来走不通的流程最后都会调用到draw(Canvas canvas)艺术来做正在的绘图,draw(Canvas canvas)格局中会调用到dispatchDraw(canvas)办法,来向下分发绘制,dispatchDraw(canvas)措施中会调用draw(Canvas
canvas, ViewGroup parent, long drawing提姆(Tim)e)。绘制就少有向下传递。

然则作为头等的DecorView就差异了,ViewRootImpl调用DecorView的draw(Canvas
canvas)方法间接打开所有view tree的绘图,DecorView的draw(Canvas
canvas)方法中调用dispatchDraw(canvas)开端向下分发绘制。一薄薄传到到view
tree的最底部。

setView(View view, WindowManager.LayoutParams attrs, View panelParentView)

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                // 将 decorView 设置给全局的 mView
                mView = view;
                ...
                // 标记已经添加了 decorView
                mAdded = true;
                ...
                // 第一次发起布局,在添加到 WindowManager 之前
                // 确保在接收其他系统事件之前完成重新布局
                requestLayout();
                ...
                // 利用 mWindowSession 以跨进程的方式向 WMS 发起一个调用,从而将DecorView 最终添加到 Window 上
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel);
                } 
                ...
            }
        }
    }


setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
方法中,首要做的事务有:

  1. 保存 DecorView
  2. 率先次调用 requestLayout() ,发起整个 View 的绘图流程
  3. 将 View 添加到 Window 上去

而在那,大家第一关心 requestLayout() 方法,因为刚刚那句代码引发了上上下下
View 的绘图。

以一张调试图来浮现一下整整流程:

图1 View Hierarchy

requestLayout()

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            // 检查当前线程
            checkThread();
            mLayoutRequested = true;
            // 调用绘制
            scheduleTraversals();
        }
    }

requestLayout() 中先检查了线程,若 OK 后调用 scheduleTraversals()

图片 1

方方面面View Hierarchy真正绘制开头是从DecorView的draw(Canvas
canvas)方法初始的,下边描述一下Activity的开行到调用到DecorView的draw(Canvas
canvas)方法的流水线:

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 TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            // 内部调用了 performTraversals()
            doTraversal();
        }
    }

scheduleTraversals() 中,其实是那般的:

scheduleTraversals() -> 调用 mTraversalRunnable -> doTraversal()
-> performTraversals()

故此最后如故要看 performTraversals()

C21598DB-BBFD-4F60-BE7C-2C9C173D74E6.png

/**1*/ ApplicationThread的onTransact方法接收到SystemServer进程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION启动Activity的Binder信息
                ↓
/**2*/ ApplicationThread.scheduleLaunchActivity() //
                ↓
/**3*/ ActivityThread.scheduleLaunchActivity() //安排启动Activity
                ↓
/**4*/ ActivityThread.handleLaunchActivity()  //处理启动Activity
                ↓
/**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume会使DecorView跟ViewRootImpl关联
                ↓
/**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
                ↓
/**7*/ ViewRootImpl.setView()  //使DecorView和ViewRootImpl关联并绘制界面
                ↓
/**8*/ ViewRootImpl.requestLayout() //请求绘制ViewTree
                ↓
/**9*/ ViewRootImpl.scheduleTraversals() // 安排遍历 
                ↓
/**10*/ ViewRootImpl.doTraversal() //
                ↓
/**11*/ ViewRootImpl.performTraversals() //执行遍历 会根据情况调用relayoutWindow performMeasure performLayout performDraw 等方法 这四个方法跟绘制是紧密相关的
                ↓
/**12*/ ViewRootImpl.performDraw() //执行绘制
                ↓
/**13*/ ViewRootImpl.draw(boolean fullRedrawNeeded)//区分是否支持硬件加速来走不同的绘制流程                
                ↓
             .........
                ↓
/**14*/ DecorView.draw(Canvas canvas)  //不管走不走硬件加速都会调到这里

performTraversals()

    private void performTraversals() {

        // 计算 Activity 中 window 的宽高等等
        ...

        if (!mStopped || mReportNextDraw) {
                boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                        (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
                if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                        || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                        updatedConfiguration) {
                    // 得到 view 宽高的规格
                    // mWidth 和 mHeight 即用来描述 Activity 窗口宽度和高度
                    // lp.width 和 lp.height 就是 DecorView 的宽高
                    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()
                            + " coveredInsetsChanged=" + contentInsetsChanged);

                     // Ask host how big it wants to be
                     // 开始执行测量工作,测量是从这里发起的
                    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;
                    }

                    // 需要再次测量的话,就再执行一遍 performMeasure
                    if (measureAgain) {
                        if (DEBUG_LAYOUT) Log.v(mTag,
                                "And hey let's measure once more: width=" + width
                                + " height=" + height);
                        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                    }

                    layoutRequested = true;
                }
            }

        ...

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            // 执行布局工作,布局是从这里发起的
            performLayout(lp, mWidth, mHeight);

        ...

        if (!cancelDraw && !newSurface) {
            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).startChangingAnimations();
                }
                mPendingTransitions.clear();
            }
            // 执行绘制工作,绘制是从这里发起的
            performDraw();
        } 

        ...

    }

performTraversals()
方法的代码很长很长,不过我们关切点就足以放在三大流程上。其他的代码因为自己能力不足,并无法挨个说出那几个代码的成效。所以大家接下去就把第一放在:

  1. getRootMeasureSpec
  2. performMeasure
  3. performLayout
  4. performDraw

==============================

全方位界面的绘图从 DecorView.draw(Canvas canvas)方法初叶紧锣密鼓!

三大流程

浅析一下View.updateDisplayListIfDirty方法中对施行dispatchDraw如故draw方法的判断条件

一、draw(canvas,parent,drawing提姆(Tim)e)和draw(canvas)成效差距

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
  /**
     * This method is called by ViewGroup.drawChild() to have each child view draw itself.
     * 此方法是被父控件ViewGroup.drawChild()调用的
     * drawChild()方法又是被ViewGroup中的dispatchDraw(Canvas canvas)方法调用的
     * This is where the View specializes rendering behavior based on layer type,
     * and hardware acceleration.
     */
    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
        /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
         *
         * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't
         * HW accelerated, it can't handle drawing RenderNodes.
         */
         //判断当前View是否支持硬件加速绘制
        boolean drawingWithRenderNode = mAttachInfo != null
                && mAttachInfo.mHardwareAccelerated
                && hardwareAcceleratedCanvas;

        ......略

        //硬件加速绘制用到的绘制节点
        RenderNode renderNode = null;
        //cpu绘制用到的绘制缓存
        Bitmap cache = null;
        //获取当前View的绘制类型 LAYER_TYPE_NONE LAYER_TYPE_SOFTWARE LAYER_TYPE_HARDWARE
        int layerType = getLayerType(); // TODO: signify cache state with just 'cache' local
        if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) {  //如果是cpu绘制类型
             if (layerType != LAYER_TYPE_NONE) {
                 // If not drawing with RenderNode, treat HW layers as SW
                 layerType = LAYER_TYPE_SOFTWARE;
                 //开始cpu绘制缓存构建
                 buildDrawingCache(true);
            }
            //获得cpu绘制缓存结果 存储在Bitmap中
            cache = getDrawingCache(true);
        }

        if (drawingWithRenderNode) { //支持硬件加速
            // Delay getting the display list until animation-driven alpha values are
            // set up and possibly passed on to the view
            //更新gpu绘制列表 保存在RenderNode中
            renderNode = updateDisplayListIfDirty();
            if (!renderNode.isValid()) {
                // Uncommon, but possible. If a view is removed from the hierarchy during the call
                // to getDisplayList(), the display list will be marked invalid and we should not
                // try to use it again.
                renderNode = null;
                //gpu绘制失败标识
                drawingWithRenderNode = false;
            }
        }

        ......略

        //cpu绘制成功并且gpu绘制失败了
        final boolean drawingWithDrawingCache = cache != null && !drawingWithRenderNode;

        ......略


        if (!drawingWithDrawingCache) { //走gpu绘制
            if (drawingWithRenderNode) { //支持gpu绘制
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                //gpu绘制收集到的DisplayList
                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
            } else { //走gpu绘制又突然不支持gpu绘制(可能是极限情况下)
                // Fast path for layouts with no backgrounds
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    //没有内容不需要绘制自己,就直接向下分发绘制子View
                    dispatchDraw(canvas);
                } else {
                    //绘制自己后再分发绘制子View
                    draw(canvas);
                }
            }
        } else if (cache != null) { //走cpu绘制且cpu绘制缓存不为null

         ......略

            //把存储cpu绘制缓存的Bitmap用canvas走cpu绘制(skia渲染引擎)
            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);

        }
         ......略

        return more;
    }

    /**
     * Manually render this view (and all of its children) to the given Canvas.
     * The view must have already done a full layout before this function is
     * called.  When implementing a view, implement
     * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
     * If you do need to override this method, call the superclass version.
     *
     * @param canvas The Canvas to which the View is rendered.
     */
    @CallSuper
    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        int saveCount;
        // Step 1 绘制背景
        if (!dirtyOpaque) {
            drawBackground(canvas);
        }

        //Step 2,必要时,保存画布的图层为褪色做准备
        final int viewFlags = mViewFlags;
        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
        if (!verticalEdges && !horizontalEdges) {
            // Step 3, 绘制View自身内容
            if (!dirtyOpaque) onDraw(canvas);

            //Step 4, 绘制子View的内容
            dispatchDraw(canvas);

            // Overlay is part of the content and draws beneath Foreground
            //Step 5, 必要时,绘制褪色边缘并恢复图层,通过        getOverlay().add(drawable); 添加图片什么的
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

            // Step 6, 绘制装饰(列如滚动条)
            onDrawForeground(canvas);

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

        ......略
}

可以看出View中的
updateDisplayListIfDirty()措施是gpu绘制的基本点,buildDrawingCache()格局是cpu绘制的首要。
updateDisplayListIfDirty()buildDrawingCache()办法都会调用到View的draw(canvas)方法,不过updateDisplayListIfDirty()措施中给draw(canvas)传的是DisplayListCanvas参数,使其抱有HWUI的效劳。buildDrawingCache()方法中给draw(canvas)办法传入的是平凡的Canvas。

也可以很清楚滴看到,对于大家开发者来说,draw(Canvas canvas, ViewGroup parent, long drawingTime)方法就是一个View的绘图入口,从那些法子中控制了走cpu绘制仍然gpu绘制。

draw(Canvas canvas)措施是现实的绘图工作,若是是gpu硬件加快绘制,则利用DisplayListCanvas画布绘制,会把绘制DisplayList保存在绘制节点RenderNode中。假使是CPU软绘制,则接纳普通的Canvas画布绘制,把绘制缓存保存在一个Bitmap中,最后会使用canvas.drawBitmap()主意应用skia渲染引擎cpu绘制缓存Bitmap中的数据。

ViewRootImpl

图片 2

二、一级DecorView硬件加快调用draw(canvas)

注意:
DecorView实际上是一个FrameLayout,FrameLayout是一个ViewGroup,ViewGroup是一个继承View的抽象类,draw(canvas)格局只在View类中有落到实处,所以调用DecorViewdraw(canvas)实在最终调用的是Viewdraw(canvas)方法。

上边已经说了,DecorView是顶级View,它的draw(canvas)方法是绘制的发端,那么在硬件加速下ViewRootImpl是怎么调用到DecorView的draw(canvas)的呢?

得从ViewRootImpl的draw(boolean fullRedrawNeeded)主意伊始分析:

/***********************************************************************        
   /**1*/ ApplicationThread的onTransact方法接收到SystemServer进程的SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION启动Activity的Binder信息
                ↓
   /**2*/ ApplicationThread.scheduleLaunchActivity() //
                   ↓
   /**3*/ ActivityThread.scheduleLaunchActivity() //安排启动Activity
                   ↓
   /**4*/ ActivityThread.handleLaunchActivity()  //处理启动Activity
                   ↓
   /**5*/ ActivityThread.handleResumeActivity() // Activity 的Resume会使DecorView跟ViewRootImpl关联
                   ↓
   /**6*/ WindowManagerGlobal.addView() //全局保存窗口的信息
                   ↓
   /**7*/ ViewRootImpl.setView()  //使DecorView和ViewRootImpl关联并绘制界面
                   ↓
   /**8*/ ViewRootImpl.requestLayout() //请求绘制ViewTree
                   ↓
   /**9*/ ViewRootImpl.scheduleTraversals() // 安排遍历 
                   ↓
   /**10*/ ViewRootImpl.doTraversal() //
                   ↓
   /**11*/ ViewRootImpl.performTraversals() //执行遍历 会根据情况调用relayoutWindow performMeasure performLayout performDraw 等方法 这四个方法跟绘制是紧密相关的
                   ↓
   /**12*/ ViewRootImpl.performDraw() //执行绘制
                   ↓
   /**13*/ 区分是否支持硬件加速来走不同的绘制流程*********************************/  

   private void draw(boolean fullRedrawNeeded) {

      ......略

        if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) { //支持硬件加速并且需要绘制
            if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {

                ......略
                //1 硬件加速调DecorView 的draw(canvas)方法的关键
                mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
            } else {

                ......略

                //2 非硬件加速调DecorView的draw(canvas)方法的关键
                if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                    return;
                }
            }
        }
               ......略
    }
}

ThreadedRenderer是5.0上为种种进度新增了一个RenderThread线程,既一个渲染线程,RenderThread线程可以有限支撑在主线程阻塞的景况下动画执行仍然流畅顺滑。就是一个异步绘制的拍卖线程。
越来越多请参见:
http://www.jianshu.com/p/bc1c1d2fadd1
http://blog.csdn.net/guoqifa29/article/details/45131099

咱俩先分析硬件加快调用DecorViewdraw(canvas)方法,先看mAttachInfo.mHardwareRenderer.draw(mView,
mAttachInfo, this)里面的流程:

/**
 *5.0新增的渲染线程
 */
public final class ThreadedRenderer {
    ......略 
    /**
     * Draws the specified view.
     *
     * @param view The view to draw.
     * @param attachInfo AttachInfo tied to the specified view.
     * @param callbacks Callbacks invoked when drawing happens.
     */
    void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {

        //1 圈起来 终点 要考的 ,其实就是更新`DecorView`的`DisplayList`
        updateRootDisplayList(view, callbacks);

    }

    //其实就是更新`DecorView`的`DisplayList`
    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {

        //更新`DecorView`的`DisplayList`
        updateViewTreeDisplayList(view);

        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
            //1 获取一个DisplayListCanvas画布
            DisplayListCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
            try {
                final int saveCount = canvas.save();
                canvas.translate(mInsetLeft, mInsetTop);
                callbacks.onHardwarePreDraw(canvas);

                canvas.insertReorderBarrier();

                //2 绘制获取到的DecorView的RenderNode
                //view.updateDisplayListIfDirty()其实是调用的DecorView的updateDisplayListIfDirty方法,
                //通过层层调用updateDisplayListIfDirty方法最终会获取整个view tree的绘制节点`RenderNode`

                canvas.drawRenderNode(view.updateDisplayListIfDirty());

                canvas.insertInorderBarrier();

                callbacks.onHardwarePostDraw(canvas);
                canvas.restoreToCount(saveCount);
                mRootNodeNeedsUpdate = false;
            } finally {
                //3 整个View tree绘制结束后回收资源
                mRootNode.end(canvas);
            }
        }
    }
  ......略
}

从地点可以看出 更新DecorViewDisplayList而调用
updateViewTreeDisplayList(view)方法,这几个办法请看:

/**
 *5.0新增的渲染线程
 */
public final class ThreadedRenderer {
    ......略
    private void updateViewTreeDisplayList(View view) {
        view.mPrivateFlags |= View.PFLAG_DRAWN;
        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                == View.PFLAG_INVALIDATED;
        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
    //其实也是更新DecorView的DisplayList而调用view.updateDisplayListIfDirty()方法
        view.updateDisplayListIfDirty();
        view.mRecreateDisplayList = false;
    }
    ......略
}

阅览那里那么可以明白,硬件加快的情状下,DecorViewupdateDisplayListIfDirty方法是根本,也是从那里调用到DecorView的的draw(canvas)方法开启绘制的,请看源代码:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {

   ......略

    /**
     * 更新一个View的绘制DisplayList保存在RenderNode返回 
     * 返回的RenderNode是被ThreadedRenderer线程的drawRenderNode(RenderNode)方法绘制的
     * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported)
     * @hide
     */
    @NonNull
    public RenderNode updateDisplayListIfDirty() {
        final RenderNode renderNode = mRenderNode;

               ......略

            //从renderNode中获取一个DisplayListCanvas
            final DisplayListCanvas canvas = renderNode.start(width, height);
            canvas.setHighContrastText(mAttachInfo.mHighContrastText);

            try {
                if (layerType == LAYER_TYPE_SOFTWARE) { //为CPU绘制draw(canvas)方法
                    buildDrawingCache(true); //创建CPU绘制缓存会调用到View的
                    Bitmap cache = getDrawingCache(true);  //保存CPU绘制缓存
                    if (cache != null) {
                        canvas.drawBitmap(cache, 0, 0, mLayerPaint); //skia绘制收集到的Bitmap缓存数据
                    }
                } else { //为硬件加速GPU绘制
                    computeScroll();

                    canvas.translate(-mScrollX, -mScrollY);
                    mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;

                    // Fast path for layouts with no backgrounds
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                        dispatchDraw(canvas); //View本身不需要绘制 直接分发给子View绘制
                        if (mOverlay != null && !mOverlay.isEmpty()) {
                            mOverlay.getOverlayView().draw(canvas);
                        }
                    } else {
                        //使用DisplayListCanvas绘制,需要的绘制会保存在DisplayList
                        draw(canvas);
                    }
                }
            } finally {
                renderNode.end(canvas);  //回收资源
                setDisplayListProperties(renderNode);
            }
        } else {
            mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
        }
        return renderNode; //返回保存有GPU绘制数据DisplayList的绘制节点renderNode
    }

   ......略

}

可以见见在View的updateDisplayListIfDirty方法中,扶助硬件加快的情景下准备好RenderNodeDisplayListCanvas后间接调用了View的draw(canvas)方法。

measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWindowWidth, final int desiredWindowHeight)

其实在 performTraversals() 中有一句代码

// Ask host how big it wants to be
windowSizeMayChange |= measureHierarchy(host, lp, res,
        desiredWindowWidth, desiredWindowHeight);

measureHierarchy 方法中早已调用了 performMeasure
来进行测量。然则功效不一,只是为了确定 window
的大小而做的测量支持。所以可以说,并不算上在三大流程中。

measureHierarchy 中,确定了 DecorView 的 MeasureSpec 。其中
childWidthMeasureSpecchildHeightMeasureSpec 即为 DecorView
对应的 MeasureSpec

// desiredWindowWidth 和 desiredWindowHeight 是屏幕的宽高
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

6383EFDF-A106-43B3-A4B4-41F7D033623F.png

小结流程:

//前提是需要支持硬件加速
ViewRootImpl.draw(boolean fullRedrawNeeded) 
                   ↓
ThreadedRenderer.draw(view,attachInfo,hardwareDrawCallbacks)                   
                   ↓           
ThreadedRenderer.updateRootDisplayList(view, callbacks)
                   ↓
DecorView.updateDisplayListIfDirty()
                   ↓  
        DecorView.draw(canvas)

getRootMeasureSpec(int windowSize, int rootDimension)

那么就来探视 getRootMeasureSpec 咯。

    private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }

代码很简短,也很易懂。

  1. 如果是 MATCH_PARENT ,那么相应的就是窗口大小;
  2. 如果是 WRAP_CONTENT ,那么不可能超越窗口大小;
  3. 稳定大小,那么就是大小就是传入的 lp.width/lp.height 了。

判断条件是:mPrivateFlags & PFLAG_SKIP_DRAW == PFLAG_SKIP_DRAW;
翻译过来就是mPrivateFlags中涵盖PFLAG_SKIP_DRAW就推行dispatchDraw,不带有就执行draw(mPrivateFlags是View中当前起效率的持有Flags标识)。那PFLAG_SKIP_DRAW是在哪个地方设置的呢?
View中有个setFlags方法,这里就是设置PFLAG_SKIP_DRAW的地方。
<pre>
void setFlags(int flags, int mask) {
……
if ((changed & DRAW_MASK) != 0) {
……
//表示将PFLAG_SKIP_DRAW放入mPrivateFlags中
mPrivateFlags |= PFLAG_SKIP_DRAW;
……
requestlayout();
invalidate(true);
}
……
}
</pre>
简言之的诠释就是:即使是View则不安装PFLAG_SKIP_DRAW,倘若是ViewGroup就设置。那也顺应地方的if分析。那setFlags是何等时候调用的吧?
在ViewGroup开始化的时候,会调用构造函数:
<pre>
public ViewGroup(Context context, AttributeSet attrs…………) {
super(context, attrs…..);
initViewGroup();
initFromAttributes(context, attrs…..);
}
</pre>
在ViewGroup的构造函数里会调用initViewGroup方法:
<pre>
private void initViewGroup() {
if (!debugDraw()) {
setFlags(WILL_NOT_DRAW, DRAW_MASK);
}
……
}
</pre>
那里就调用到了setFlags并传递参数(WILL_NOT_DRAW,
DRAW_MASK),这也刚好适合setFlags中if的判断标准。

三、一流DecorView非硬件加快调用draw(canvas)

从下面的ViewRootImpldraw(boolean fullRedrawNeeded)主意中可以见到,如若是CPU绘制,就会走drawSoftware()艺术。那么我们看一下drawSoftware()中是怎么调到DecorView的draw(canvas)方法的:

ViewRootImpl``drawSoftware()方法:

    /**
     * @return true if drawing was successful, false if an error occurred
     */
    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
        final Canvas canvas;

            ......略

            //从Surface中获取一个普通的Canvas
            canvas = mSurface.lockCanvas(dirty);

            ......略

            //调用DecorView的draw(canvas)方法
            mView.draw(canvas);

            ......略

        return true;
    }

ViewGroup

小结流程:

//前提是不支持硬件加速
ViewRootImpl.draw(boolean fullRedrawNeeded) 
                   ↓
ViewRootImpl.drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)                  
                   ↓  
        DecorView.draw(canvas)

getChildMeasureSpec(int spec, int padding, int childDimension)

顺便,大家把经常自定义 ViewGroup 计算子 View 测量规格的
getChildMeasureSpec 方法也共同来探视:

    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        // 父容器的 mode
        int specMode = MeasureSpec.getMode(spec);
        // 父容器的 size
        int specSize = MeasureSpec.getSize(spec);
        // 子 view 可以使用空间,即父容器的 size - padding
        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

上边的 switch/case
代码相比较不难,而且便于了解。大家得以整理为一张表格(该表格来自于《Android开发方式探索》):

图片 3

measurespec

在那里,大家总括一下。对于 DecorView 来说,其 MeasureSpec
是由窗口的分寸和自己的 LayoutParams 来共同决定的;而对于普通的 View
来说,其 MeasureSpec 是由父容器的 MeasureSpec 和本人的
LayoutParams 共同决定的。

DecorView的draw(canvas)方法调用统计:

DecorView作为甲级View的存在,它的绘图是由ViewRootImpl判断是CPU仍旧GPU绘制然后调用DecorViewdraw(canvas)方法,开启所有界面的绘图。

任何的View,都有是团结的父控件调用
draw(canvas,parent,drawingTime)格局,在draw(canvas,parent,drawing提姆e)方法中判断当前View是CPU仍然GPU绘制然后调用draw(canvas)

measure过程

四、非一流View硬件加速draw(canvas, parent, drawing提姆e)调用draw(Canvas canvas)

地方我早就说了,整个界面的绘图是从DecorViewdraw(canvas)办法开端,普通View的绘图是从draw(canvas, parent, drawingTime)开始。
View的draw(canvas, parent, drawingTime)、draw(Canvas
canvas)和updateDisplayListIfDirty()四个主意本身那里就不粘贴了,上早已有了,直接给出流程吧:

//GPU绘制
Vew.draw(Canvas canvas, ViewGroup parent, long drawingTime)
     ↓
Vew.updateDisplayListIfDirty()
     ↓
Vew.draw(displayListCanvas)

ViewRootImpl

五、非一流View非硬件加速draw(canvas, parent, drawing提姆e)调用draw(Canvas canvas)

先交由CPU绘制流程:

//CPU绘制
Vew.draw(Canvas canvas, ViewGroup parent, long drawingTime)
     ↓
Vew.buildDrawingCache(boolean autoScale)
     ↓
Vew.buildDrawingCacheImpl(boolean autoScale)
     ↓ 
Vew.draw(displayListCanvas)

draw(canvas, parent,
drawing提姆e)方法调用到buildDrawingCache在上头的代码中得以看来,那里就看一下buildDrawingCachebuildDrawingCacheImpl方法:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {

   ......略

       public void buildDrawingCache(boolean autoScale) {
        if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                mDrawingCache == null : mUnscaledDrawingCache == null)) {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW,
                        "buildDrawingCache/SW Layer for " + getClass().getSimpleName());
            }
            try {
                //执行CPU绘制缓存创建
                buildDrawingCacheImpl(autoScale);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    }

       ......略

    /**
     * private, internal implementation of buildDrawingCache, used to enable tracing
     */
    private void buildDrawingCacheImpl(boolean autoScale) {

           ......略

            //保存CPU绘制缓存的Bitmap
            Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;

           ......略
        if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {

            ......略

            try {
                //如果缓存Bitmap为空就重新创建和赋值
                bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
                        width, height, quality);
                bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
                if (autoScale) {
                    mDrawingCache = bitmap;
                } else {
                    mUnscaledDrawingCache = bitmap;
                }
                if (opaque && use32BitCache) bitmap.setHasAlpha(false);
            } catch (OutOfMemoryError e) {

                ......略
            }

            clear = drawingCacheBackgroundColor != 0;
        }

        Canvas canvas;
        if (attachInfo != null) { //处理Canvas
            canvas = attachInfo.mCanvas;
            if (canvas == null) {
                canvas = new Canvas();
            }
            //canvas的Bitmap设置为我们创建的缓存Bitmap
            canvas.setBitmap(bitmap);

            ......略

        } else {
            // This case should hopefully never or seldom happen
            canvas = new Canvas(bitmap);
        }
            ......略

        // Fast path for layouts with no backgrounds
        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;

            dispatchDraw(canvas); //自己不需要绘制,直接分发子View绘制

            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().draw(canvas);
            }
        } else {
            //自己需要绘制,然后再分发子View绘制,所有的绘制都会画在缓存Bitmap上面
            draw(canvas);
        }

            ......略
    }

   ......略

   }

performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)

浅析 measure 进程,我们的起源就是在 ViewRootImpl
performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)
方法中:

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            // 进行测量
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

performMeasure 中调用了 measure 方法。说到底,DecorView
只是一个为此大家又要跻身 View 类中去看下。

制图总括:

无论是是永葆照旧不支持硬件加快,都会调用到View的draw(canvas)方法。
只是硬件加快的处境下为DisplayListCanvas画布,得到的DisplayList数码保存在每一个View的绘图节点RenderNode中,最终交给DisplayListCanvasdrawRenderNode(renderNode)方法处理渲染操作。
非硬件加速的情景下,会把具有的绘图缓存数据保存到一个缓存Bitmap中,然后由Canvas.drawBitmap(cache,
0.0f, 0.0f, mLayerPaint)负责把多少交到skia渲染。

DisplayList构建分析请看:http://www.jianshu.com/p/7bf306c09c7e

View

整体测量定位绘制流程统计总括:

其实draw流程相对于MeasureLayout来说更加一些,为何这么说?若是你了然view的整套工艺流程就知晓,MeasureLayout流程在ViewViewGroup那七个基本控件中都从未实际贯彻,而View的绘图在View这一个基本的类中都贯彻了,而且在View的raw(Canvas
canvas, ViewGroup parent, long drawing提姆e)
方法中区分了cpugpu绘制来走不一样的流水线。View的draw(Canvas
canvas)方法已毕了切实的6个绘制步骤,ViewGroup中的dispatchDraw(Canvas
canvas)方法落成了实际的子View的绘图分发。

干什么是那样?

因为为主旨的ViewViewGroup控件不能够决定具体的旗帜,那里说的规范更加多偏重于控件的排列,大小宽高,之间的周旋地点,那几个属性都是由,MeasureLayout流程控制的。所以无论是你怎么走MeasureLayout流程,其draw绘制都是均等的。draw绘制出来的旗帜是由现实的LinearLayoutFrameLayoutRelativeLayoutRecyclerView等等控件的MeasureLayout流程控制的。所以MeasureLayout流程必要在切实可行的控件中现实落到实处。

理所当然下边说的是系统的控件,LinearLayoutFrameLayout这么些,自定义的View或者ViewGroup的流程那就全盘由开发者自己说了。

不晓得绘制流程的请看:http://blog.csdn.net/yanbober/article/details/46128379

measure(int widthMeasureSpec, int heightMeasureSpec)

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        ...

        if (forceLayout || needsLayout) {
            ...
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // measure ourselves, this should set the measured dimension flag back
                // 调用 onMeasure
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                long value = mMeasureCache.valueAt(cacheIndex);
                // Casting a long to int drops the high 32 bits, no mask needed
                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            }

            ...
    }

Viewmeasure 方法内部是调用了 onMeasure
。所以大家还要随着跟进到 onMeasure 中才行。另外, measure 方法是用
final 修饰的,所以子类是力不从心开展重写的。

硬件绘制软件绘不足之处

现阶段硬件绘制软件绘图都存在不足,比如(来自:http://blog.csdn.net/jxt1234and2010/article/details/47326411):
脏区域识别之后并没有丰盛地优化 。

软件渲染时,固然限制了渲染区域,但具有ViewonDraw方式一个不丢的实施了五回。
硬件渲染时,防止了没刷新的ViewonDraw措施创新呈现列表,但显得列表中的命令依旧一个不落的在全显示屏上推行了一回。

一个比较便于想到的优化方案就是为主流程中的View建立一个R-Tree索引,invalidate这一接口修改为可以流传一个矩形范围R,更新时,利用R-Tree索引找出含有R的具有叶子View,令那几个View在R范围重绘五回即可。

那几个槽点其实影响倒不是很大,一大半情景下View不多,且一旦出现性能问题,基本上都是一半上述的显示器刷新。

对不住大家了,有一个硬件绘制很重点大方法dispatchGetDisplayList()没讲到,抱歉了。罪过罪过呀!

FrameLayout

onMeasure(int widthMeasureSpec, int heightMeasureSpec)

此地小提一下,大家都掌握 DecorView 其实是一个 FrameLayout ,所以
onMeasure 应该在 FrameLayout 中去看:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();
        // 判断当前 framelayout 布局的宽高是否至少一个是 match_parent 或者精确值 ,如果是则置 measureMatchParent 为 false .
        final boolean measureMatchParentChildren =
                MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
                MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
        mMatchParentChildren.clear();

        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        // 遍历不为 GONE 的子 view
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (mMeasureAllChildren || child.getVisibility() != GONE) {
                // 对每一个子 View 进行测量
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                // 寻找子 View 中宽高的最大者,因为如果 FrameLayout 是 wrap_content 属性
                // 那么它的宽高取决于子 View 中的宽高最大者
                maxWidth = Math.max(maxWidth,
                        child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,
                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                // 如果 FrameLayout 为 wrap_content 且 子 view 的宽或高为 match_parent ,那么就添加到 mMatchParentChildren 中
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT ||
                            lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }

        // Account for padding too
        maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
        maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();

        // Check against our minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        // Check against our foreground's minimum height and width
        final Drawable drawable = getForeground();
        if (drawable != null) {
            maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
            maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
        }
        //设置测量结果
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));

        // 子View中设置为match_parent的个数
        count = mMatchParentChildren.size();
        // 若 FrameLayout 为 wrap_content 且 count > 1
        if (count > 1) {
            for (int i = 0; i < count; i++) {
                final View child = mMatchParentChildren.get(i);
                final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                // 如果子 View 的宽度是 match_parent 属性,那么对 childWidthMeasureSpec 修改:
                // 把 widthMeasureSpec 的宽度修改为:framelayout总宽度 - padding - margin,模式设置为 EXACTLY
                final int childWidthMeasureSpec;
                if (lp.width == LayoutParams.MATCH_PARENT) {
                    final int width = Math.max(0, getMeasuredWidth()
                            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()
                            - lp.leftMargin - lp.rightMargin);
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
                            width, MeasureSpec.EXACTLY);
                } else {
                    // 否则就按照正常的来就行了
                    childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
                            lp.leftMargin + lp.rightMargin,
                            lp.width);
                }

                // 高度同理
                final int childHeightMeasureSpec;
                if (lp.height == LayoutParams.MATCH_PARENT) {
                    final int height = Math.max(0, getMeasuredHeight()
                            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()
                            - lp.topMargin - lp.bottomMargin);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
                            height, MeasureSpec.EXACTLY);
                } else {
                    childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
                            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
                            lp.topMargin + lp.bottomMargin,
                            lp.height);
                }
                //对于这部分的子 View 需要重新进行 measure 过程
                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
            }
        }
    }

万一下面 FrameLayoutonMeasure
流程没看懂的话也没涉及。其实看来主要的就唯有遍历
child.measure(childWidthMeasureSpec, childHeightMeasureSpec)
这么些方法,那是将父容器的 measure 进度传递到子 View 中。

ViewGroup

measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)

可能有些人也有疑难,在地点
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0)
后也没来看有 child.measure 的方法啊,那是因为在
measureChildWithMargins 中内部调用了 child.measure

    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        // getChildMeasureSpec 我们上面分析过了
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);
        // measure 传递给子 View
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

那下明白了吗?父容器就是遍历调用了 child.measure 那一个艺术将 measure
进程传递给每一个子 View 的。即便差别的父容器 onMeasure
方法都差距,可是同样的是,他们都会遍历调用 child.measure

View

onMeasure(int widthMeasureSpec, int heightMeasureSpec)

地方大家也讲过,measure 方法内部其实是调用了 onMeasure ,所以子 View
被父容器调用了 measure 后,也会调用属于自己的 onMeasure
方法。那么我们就径直看向 ViewonMeasure 方法:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

onMeasure 方法唯有一句代码,所以最紧要就是
getDefaultSize(int size, int measureSpec) 咯。

getSuggestedMinimumWidth() 内部逻辑:

  1. 若没有设置背景,就是 android:minWidth 的值;
  2. 若有设置背景,就是 max(android:minWidth, 背景 Drawable 的原来宽度)

getSuggestedMinimumHeight() 也是同理。

getDefaultSize(int size, int measureSpec)

    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        // 直接返回 specSize
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

从地点大家可以看看:

  • 一旦 UNSPECIFIED ,则一向回到的就是
    getSuggestedMinimumWidth/getSuggestedMinimumHeight 的值;
  • 若是 AT_MOST/EXACTLY ,直接用的就是 specSize 。

而依照我们事先总计出来的表可见,只要 view 不点名固定大小,那么不论是
AT_MOST 如故 EXACTLY ,都是根据 parentSize 来的。

那也是为何大家在自定义 View 时,倘使不重写
onMeasure(int widthMeasureSpec, int heightMeasureSpec) ,wrap_content
和 match_parent 效果一样的缘故。

小结

大家把 measure 进程的代码流程理一下:

ViewRootImpl.performTraversals -> ViewRootImpl.performMeasure ->
DecorView.measure -> DecorView.onMeasure ->
DecorView.measureChildWithMargins -> ViewGroup.measure ->
ViewGroup.onMeasure -> ViewGroup.measureChildWithMargins -> …
-> View.measure -> View.onMeasure

注:DecorView 其实就是 FrameLayout

layout过程

ViewRootImpl

performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)

在上头分析过,layout 进度是从 ViewRootImpl 中的
performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)
开始的。

    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
        mLayoutRequested = false;
        mScrollMayChange = true;
        mInLayout = true;

        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 就是 DecorView,调用了 layout 方法开始布局
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

            mInLayout = false;
            // mLayoutRequesters 为需要重新请求布局的 view 集合数
            int numViewsRequestingLayout = mLayoutRequesters.size();

            // 下面的代码主要用于若有请求重新布局的 view ,那么再进行重新布局
            if (numViewsRequestingLayout > 0) {
                // requestLayout() was called during layout.
                // If no layout-request flags are set on the requesting views, there is no problem.
                // If some requests are still pending, then we need to clear those flags and do
                // a full request/measure/layout pass to handle this situation.
                ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
                        false);
                if (validLayoutRequesters != null) {
                    // Set this flag to indicate that any further requests are happening during
                    // the second pass, which may result in posting those requests to the next
                    // frame instead
                    mHandlingLayoutInLayoutRequest = true;

                    // view 请求布局,进行重新测量和布局
                    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();
                    }

                    // 对整个View树进行重新测量
                    measureHierarchy(host, lp, mView.getContext().getResources(),
                            desiredWindowWidth, desiredWindowHeight);
                    mInLayout = true;

                    // 进行第二次布局
                    host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

                    mHandlingLayoutInLayoutRequest = false;

                    // Check the valid requests again, this time without checking/clearing the
                    // layout flags, since requests happening during the second pass get noop'd
                    validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
                    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();
                                }
                            }
                        });
                    }
                }

            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        mInLayout = false;
    }

大旨可以,performLayout 是通过调用 DecorView 的 layout
方法来向下传递布局的。所以大家应该继承追踪 FrameLayoutlayout
方法,其实就是 ViewGrouplayout 方法。

ViewGroup

layout(int l, int t, int r, int b)

FrameLayoutlayout 是父类 ViewGroup 完成的,添加了 final
修饰符,不能被重写:

@Override
public final void layout(int l, int t, int r, int b) {
    if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
        if (mTransition != null) {
            mTransition.layoutChange(this);
        }
        // 调用 view 的 layout 方法
        super.layout(l, t, r, b);
    } else {  
        // record the fact that we noop'd it; request layout when transition finishes
        mLayoutCalledWhileSuppressed = true;
    }
}

ViewGrouplayout 方法中又调用了父类的不二法门
super.layout(l, t, r, b) 。所以大家又要到 View 类中去看。

View

layout(int l, int t, int r, int b)

    @SuppressWarnings({"unchecked"})
    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);

        // 如果视图的大小和位置发生变化,会调用onLayout()
        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;
    }

地点的代码中做了这几件事:

  1. 设置当前布局中的两极分化;
  2. 调用 setFrame 来设置新的终极地方;
  3. 调用 onLayout 方法;
  4. 回调布局地点变动监听器;

setOpticalFrame(int left, int top, int right, int bottom)

咱俩先来看 setOpticalFrame 方法:

private boolean setOpticalFrame(int left, int top, int right, int bottom) {
    Insets parentInsets = mParent instanceof View ?
            ((View) mParent).getOpticalInsets() : Insets.NONE;
    Insets childInsets = getOpticalInsets();
    // 调用 setFrame 方法
    return setFrame(
            left   + parentInsets.left - childInsets.left,
            top    + parentInsets.top  - childInsets.top,
            right  + parentInsets.left + childInsets.right,
            bottom + parentInsets.top  + childInsets.bottom);
}

其实在 setOpticalFrame 的中间也是调用 setFrame 方法的。

setFrame(int left, int top, int right, int bottom)

    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);

            // 设置 view 的上下左右,赋予最新的值
            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;
            }

            // Android无障碍辅助功能通知
            notifySubtreeAccessibilityStateChangedIfNeeded();
        }
        return changed;
    }

先回根据新旧的宽高进行相比,来确定是或不是高低被改动了。即使是,会回调
sizeChange(newWidth, newHeight, oldWidth, oldHeight)
方法,这几个方法是否很熟习呢?

然后还会把那信息布告给 AccessibilityService 无障碍服务。

最后回到布局是不是改变的 boolean 值。

FrameLayout

onLayout(boolean changed, int left, int top, int right, int bottom)

跟着,根据布局改变值 changed 会调用 onLayout 方法。

onLayout 方法在 View/ViewGroup
都是空的,是亟需子类来落到实处的。所以大家仍然要看 FrameLayout 中的
onLayout

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        layoutChildren(left, top, right, bottom, false /* no force left gravity */);
    }

onLayout 中调用了 layoutChildren 方法。

layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity)

    void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
        final int count = getChildCount();

        final int parentLeft = getPaddingLeftWithForeground();
        final int parentRight = right - left - getPaddingRightWithForeground();

        final int parentTop = getPaddingTopWithForeground();
        final int parentBottom = bottom - top - getPaddingBottomWithForeground();

        // 遍历子 view
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                // 子 view 的宽高
                final int width = child.getMeasuredWidth();
                final int height = child.getMeasuredHeight();

                int childLeft;
                int childTop;

                // 得到子 view 的 gravity
                int gravity = lp.gravity;
                if (gravity == -1) {
                    gravity = DEFAULT_CHILD_GRAVITY;
                }

                final int layoutDirection = getLayoutDirection();
                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
                final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;

                // 根据不同的 gravity 来计算 childLeft
                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                    case Gravity.CENTER_HORIZONTAL:
                        childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
                        lp.leftMargin - lp.rightMargin;
                        break;
                    case Gravity.RIGHT:
                        if (!forceLeftGravity) {
                            childLeft = parentRight - width - lp.rightMargin;
                            break;
                        }
                    case Gravity.LEFT:
                    default:
                        childLeft = parentLeft + lp.leftMargin;
                }


                // 根据不同的 gravity 来计算 childTop
                switch (verticalGravity) {
                    case Gravity.TOP:
                        childTop = parentTop + lp.topMargin;
                        break;
                    case Gravity.CENTER_VERTICAL:
                        childTop = parentTop + (parentBottom - parentTop - height) / 2 +
                        lp.topMargin - lp.bottomMargin;
                        break;
                    case Gravity.BOTTOM:
                        childTop = parentBottom - height - lp.bottomMargin;
                        break;
                    default:
                        childTop = parentTop + lp.topMargin;
                }
                // 调用子 view 的 layout 方法
                child.layout(childLeft, childTop, childLeft + width, childTop + height);
            }
        }
    }

简易来说,在 layoutChildren 中,遍历所有可知的子 View
,然后拿走它们的宽高。

再根据不相同的 gravity 来计量 childLeft 和 childTop ,末了调用
child.layout 来向子 View 传递下去。

小结

大家把 layout 进程的代码流程理一下:

ViewRootImpl.performTraversals -> ViewRootImpl.performLayout ->
DecorView(ViewGroup).layout -> View.layout ->
DecorView(FrameLayout).onLayout ->
DecorView(FrameLayout).layoutChildren -> ViewGroup.layout ->
View.layout -> ViewGroup.onLayout -> … -> View.layout ->
View.onLayout

注:

  • ViewGroup.onLayout 是空虚方法,根据分化的 ViewGroup
    都有两样的贯彻形式。不过同样的是,都会遍历调用 child.layout 方法;
  • View.onLayout 是空方法;

draw过程

末尾一个,draw 进程。 draw 进程应该来说是比较不难的。

ViewRootImpl

performDraw()

先是源点是 performDraw() 方法。

    private void performDraw() {
        if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
            return;
        }

        final boolean fullRedrawNeeded = mFullRedrawNeeded;
        mFullRedrawNeeded = false;

        mIsDrawing = true;
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
        try {
            // 调用 draw 方法,fullRedrawNeeded 为是否重新绘制全部视图
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }

        ...

    }

一经是率先次绘制视图,那么泾渭分明应该绘制所有的视图,fullRedrawNeeded
参数就为 true
;反之即使由于某些原因,导致了视图重绘,那么就平素不必要绘制所有视图,即为
false 。

draw(boolean fullRedrawNeeded)

performDraw() 内部又调用了私有方法 draw(boolean fullRedrawNeeded) :

    private void draw(boolean fullRedrawNeeded) {

        ...

        // dirty 表示需要绘制的区域
        final Rect dirty = mDirty;
        if (mSurfaceHolder != null) {
            // The app owns the surface, we won't draw.
            dirty.setEmpty();
            if (animating && mScroller != null) {
                mScroller.abortAnimation();
            }
            return;
        }

        // 如果需要全部绘制,那么 dirty 就是整个屏幕了
        if (fullRedrawNeeded) {
            mAttachInfo.mIgnoreDirtyState = true;
            dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
        }

        ...

        // 调用 drawSoftware ,把绘制区域 dirty 传入
        if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
            return;
        }

        ...

    }

在规定了绘图的区域 dirty 之后,调用了
drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty)

drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty)

    private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {

        // Draw with software renderer.
        final Canvas canvas;
        try {
            final int left = dirty.left;
            final int top = dirty.top;
            final int right = dirty.right;
            final int bottom = dirty.bottom;

            //锁定画布,由 dirty 区域决定
            canvas = mSurface.lockCanvas(dirty);

            // The dirty rectangle can be modified by Surface.lockCanvas()
            //noinspection ConstantConditions
            if (left != dirty.left || top != dirty.top || right != dirty.right
                    || bottom != dirty.bottom) {
                attachInfo.mIgnoreDirtyState = true;
            }

            // TODO: Do this in native
            canvas.setDensity(mDensity);
        } catch (Surface.OutOfResourcesException e) {
            handleOutOfResourcesException(e);
            return false;
        } catch (IllegalArgumentException e) {
            Log.e(mTag, "Could not lock surface", e);
            // Don't assume this is due to out of memory, it could be
            // something else, and if it is something else then we could
            // kill stuff (or ourself) for no reason.
            mLayoutRequested = true;    // ask wm for a new surface next time.
            return false;
        }

        try {
            if (DEBUG_ORIENTATION || DEBUG_DRAW) {
                Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
                        + canvas.getWidth() + ", h=" + canvas.getHeight());
                //canvas.drawARGB(255, 255, 0, 0);
            }

            // If this bitmap's format includes an alpha channel, we
            // need to clear it before drawing so that the child will
            // properly re-composite its drawing on a transparent
            // background. This automatically respects the clip/dirty region
            // or
            // If we are applying an offset, we need to clear the area
            // where the offset doesn't appear to avoid having garbage
            // left in the blank areas.
            if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
            }

            dirty.setEmpty();
            mIsAnimating = false;
            mView.mPrivateFlags |= View.PFLAG_DRAWN;

            if (DEBUG_DRAW) {
                Context cxt = mView.getContext();
                Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
                        ", metrics=" + cxt.getResources().getDisplayMetrics() +
                        ", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
            }
            try {
                canvas.translate(-xoff, -yoff);
                if (mTranslator != null) {
                    mTranslator.translateCanvas(canvas);
                }
                canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
                attachInfo.mSetIgnoreDirtyState = false;

                // 调用 View 的 draw 方法
                mView.draw(canvas);

                drawAccessibilityFocusedDrawableIfNeeded(canvas);
            } finally {
                if (!attachInfo.mSetIgnoreDirtyState) {
                    // Only clear the flag if it was not set during the mView.draw() call
                    attachInfo.mIgnoreDirtyState = false;
                }
            }
        } finally {
            try {
                surface.unlockCanvasAndPost(canvas);
            } catch (IllegalArgumentException e) {
                Log.e(mTag, "Could not unlock surface", e);
                mLayoutRequested = true;    // ask wm for a new surface next time.
                //noinspection ReturnInsideFinallyBlock
                return false;
            }

            if (LOCAL_LOGV) {
                Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
            }
        }
        return true;
    }

View

draw(Canvas canvas)

后来调用了 Viewdraw(Canvas canvas)

    public void draw(Canvas canvas) {
        final int privateFlags = mPrivateFlags;
        final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
        mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

        /*
         * 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
         *      3. Draw view's content
         *      4. Draw children
         *      5. If necessary, draw the fading edges and restore layers
         *      6. Draw decorations (scrollbars for instance)
         */

        // 第一步,画背景
        int saveCount;

        if (!dirtyOpaque) {
            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) {
            // 第三步,画自己的内容
            if (!dirtyOpaque) onDraw(canvas);

            // 第四步,画自己子 view 的内容
            dispatchDraw(canvas);

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

            // 第六步,绘制View的装饰,比如 scrollbar 等 (foreground, scrollbars)
            onDrawForeground(canvas);

            // 做完了,直接返回 we're done...
            return;
        }

        /*
         * Here we do the full fledged routine...
         * (this is an uncommon case where speed matters less,
         * this is why we repeat some of the tests that have been
         * done above)
         */

        boolean drawTop = false;
        boolean drawBottom = false;
        boolean drawLeft = false;
        boolean drawRight = false;

        float topFadeStrength = 0.0f;
        float bottomFadeStrength = 0.0f;
        float leftFadeStrength = 0.0f;
        float rightFadeStrength = 0.0f;

        // 第二步,保存 canvas 图层
        int paddingLeft = mPaddingLeft;

        final boolean offsetRequired = isPaddingOffsetRequired();
        if (offsetRequired) {
            paddingLeft += getLeftPaddingOffset();
        }

        int left = mScrollX + paddingLeft;
        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
        int top = mScrollY + getFadeTop(offsetRequired);
        int bottom = top + getFadeHeight(offsetRequired);

        if (offsetRequired) {
            right += getRightPaddingOffset();
            bottom += getBottomPaddingOffset();
        }

        final ScrollabilityCache scrollabilityCache = mScrollCache;
        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
        int length = (int) fadeHeight;

        // clip the fade length if top and bottom fades overlap
        // overlapping fades produce odd-looking artifacts
        if (verticalEdges && (top + length > bottom - length)) {
            length = (bottom - top) / 2;
        }

        // also clip horizontal fades if necessary
        if (horizontalEdges && (left + length > right - length)) {
            length = (right - left) / 2;
        }

        if (verticalEdges) {
            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
            drawTop = topFadeStrength * fadeHeight > 1.0f;
            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
            drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
        }

        if (horizontalEdges) {
            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
            drawLeft = leftFadeStrength * fadeHeight > 1.0f;
            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
            drawRight = rightFadeStrength * fadeHeight > 1.0f;
        }

        saveCount = canvas.getSaveCount();

        int solidColor = getSolidColor();
        if (solidColor == 0) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }

            if (drawBottom) {
                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
            }

            if (drawLeft) {
                canvas.saveLayer(left, top, left + length, bottom, null, flags);
            }

            if (drawRight) {
                canvas.saveLayer(right - length, top, right, bottom, null, flags);
            }
        } else {
            scrollabilityCache.setFadeColor(solidColor);
        }

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        dispatchDraw(canvas);

        // 第五步,绘制边缘效果和恢复图层
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;

        if (drawTop) {
            matrix.setScale(1, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, right, top + length, p);
        }

        if (drawBottom) {
            matrix.setScale(1, fadeHeight * bottomFadeStrength);
            matrix.postRotate(180);
            matrix.postTranslate(left, bottom);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, bottom - length, right, bottom, p);
        }

        if (drawLeft) {
            matrix.setScale(1, fadeHeight * leftFadeStrength);
            matrix.postRotate(-90);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(left, top, left + length, bottom, p);
        }

        if (drawRight) {
            matrix.setScale(1, fadeHeight * rightFadeStrength);
            matrix.postRotate(90);
            matrix.postTranslate(right, top);
            fade.setLocalMatrix(matrix);
            p.setShader(fade);
            canvas.drawRect(right - length, top, right, bottom, p);
        }

        canvas.restoreToCount(saveCount);

        // 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);
    }

draw 进度大致有上面几步:

  1. 绘图背景:background.draw(canvas)
  2. 封存当前的图层音信(一般的话跳过);
  3. 制图自己:onDraw(canvas)
  4. 绘制children:dispatchDraw(canvas)
  5. 绘制边缘效果,复苏图层(一般的话跳过);
  6. 绘制前景装饰:onDrawForeground(canvas)

在那里,大家三番五次看一下 dispatchDraw(Canvas canvas)
方法,那些形式是向子 View 分发绘制流程的。

因为 View 没有子 View ,所以 dispatchDraw(Canvas canvas)
方法是空的,所以大家要到 ViewGroup 中去探视。

ViewGroup

dispatchDraw(Canvas canvas)

    @Override
    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();

            // 遍历子 view 
            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());
            }
        }

        int clipSaveCount = 0;
        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
        if (clipToPadding) {
            clipSaveCount = canvas.save();
            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();
        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) {
                    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) {
                // 调用 drawChild 来绘制子 view
                more |= drawChild(canvas, child, drawingTime);
            }
        }
        ...
    }

dispatchDraw(Canvas canvas) 中,遍历子 View ,然后调用
drawChild(Canvas canvas, View child, long drawingTime) 方法来推行子
View 的绘图流程。

drawChild(Canvas canvas, View child, long drawingTime)

    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

发现在 drawChild(Canvas canvas, View child, long drawingTime)
中依然调用了 draw(Canvas canvas, ViewGroup parent, long drawingTime)
方法。不过那些 draw(Canvas canvas, ViewGroup parent, long drawingTime)
和方面的 draw(Canvas canvas) 参数差别,所以不是同一个格局。

View

draw(Canvas canvas, ViewGroup parent, long drawingTime)

    boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {

        ...

        // 如果没有绘制缓存
        if (!drawingWithDrawingCache) {
            if (drawingWithRenderNode) {
                mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
            } else {
                // Fast path for layouts with no backgrounds
                // 如果设置了 willNotDraw 为 true ,那么不会绘制自己,直接跳过,优化绘制性能
                // View 默认是 false ,ViewGroup 默认是 true ,直接让自己的子 View 进入绘制
                if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    dispatchDraw(canvas);
                } else {
                    // 调用 draw 方法
                    draw(canvas);
                }
            }
        } else if (cache != null) {
            // 有缓存就用缓存绘制
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
            if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) {
                // no layer paint, use temporary paint to draw bitmap
                Paint cachePaint = parent.mCachePaint;
                if (cachePaint == null) {
                    cachePaint = new Paint();
                    cachePaint.setDither(false);
                    parent.mCachePaint = cachePaint;
                }
                cachePaint.setAlpha((int) (alpha * 255));
                canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
            } else {
                // use layer paint to draw the bitmap, merging the two alphas, but also restore
                int layerPaintAlpha = mLayerPaint.getAlpha();
                if (alpha < 1) {
                    mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
                }
                canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
                if (alpha < 1) {
                    mLayerPaint.setAlpha(layerPaintAlpha);
                }
            }
        }

        ...

    }

draw(Canvas canvas, ViewGroup parent, long drawingTime)
中,若没有缓存的话:

  • willNotDraw 设置为 false 的话,那么调用 draw(canvas)
  • 不然间接调用 dispatchDraw(canvas) 分发给子 View ,一般适用于
    ViewGroup ;

willNotDraw 代表一个 View
不要求绘制任何内容的话,那么系统会跳过,举行性能上的优化。

到那边,就调用了子 View 的 draw(Canvas canvas)
方法,从而完成了绘图过程的向下传递。

小结

咱俩把 draw 进程的代码流程理一下:

ViewRootImpl.performTraversals -> ViewRootImpl.performDraw ->
ViewRootImpl.draw(boolean fullRedrawNeeded) ->
ViewRootImpl.drawSoftware -> DecorView(View).draw(Canvas canvas)
-> DecorView(ViewGroup).dispatchDraw ->
DecorView(ViewGroup).drawChild -> ViewGroup(View).draw(Canvas canvas,
ViewGroup parent, long drawingTime) -> ViewGroup.dispatchDraw ->
ViewGroup.drawChild -> ViewGroup.draw(Canvas canvas, ViewGroup
parent, long drawingTime) -> … -> View.draw(Canvas canvas) ->
View.onDraw -> View.dispatchDraw

注:

  • 其中 View.dispatchDraw 为空已毕;
  • DecorView 在 draw(Canvas canvas) 的措施内不会调用 onDraw 方法;
  • ViewGroup 不会调用 draw(Canvas canvas) 方法;

最后

全体来说,三个流程中最主要照旧 measure
过程较复杂。其余的多少个流程全部上来说照旧比较清晰不难的。

可以说 View 工作的三大流程是每一位 Android
开发者都不可能不驾驭的。从前纵然也询问,不过没有写成博客好好捋一下,现在到底成功了,篇幅真的太长了。
\_

除此以外,除了要求驾驭那三大流程外,还索要领悟 requestLayout
invalidate 等办法的法则。这么些东西等空闲了我理一理再写出来给大家吧。

昨日就这么了,即使有不懂的地方可以在底下留言。

References

相关文章