view -draw过程

1.view中包含的绘制元素.

view背景,任何view都有背景,可以是图片.色值或者drawable资源
视图本身内容. 在ondraw中绘制
渐变边框,本质是一个shader对象
滚动条,紧紧显示滚动的状态,任何的view都有滚动条.

2.设置思路

2.png

和之前的layout,measure过程都是类似的.draw方法不建议子视图重写. 首先是mView的draw函数被调用,一般来说mView就是窗户的根视图,对于Activity就是PhoneWindow.DecorView对象. 会调用View的draw方法,然后在onDraw中完成界面内容的绘制,如果还有子视图.就调用dispatchDraw函数,dispatchDraw由viewgroup实现了.也不建议我们来重写他.该方法内部循环调用drawchild()方法,其实就是调用子视图的draw方法完成绘制. 其实draw的过程就是把canvas从根部局不断剪裁,然后传给所有子视图利用canvas进行绘制的过程.

3.ViewRootImpl 中draw 的主要过程就是得到绘图用的Canvas.设置屏幕密度.调用mView.draw()函数.

4.View中draw()函数内部流程.

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)  绘值装饰(例如滚动条)
         */

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

        if (!dirtyOpaque) {//1.dirtyOpaque表示dirty区是否透明,只有透明才需要绘制背景.
            drawBackground(canvas);//绘制背景时会对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) {//2表示没有渐变.跳过第二步
            // Step 3, draw the content
            if (!dirtyOpaque) onDraw(canvas);//3视图在这里实现自己内容的绘制

            // Step 4, draw the children
            dispatchDraw(canvas); //4viewgroup实现这个方法来绘制子视图

            // Step 6, draw decorations (scrollbars)
            onDrawScrollBars(canvas);//5绘制滚动条等装饰内容

            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().dispatchDraw(canvas);
            }

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

      // Step 2,这里是对渐变框计算的数值的处理,代码不看了

        // Step 3, draw the content
        if (!dirtyOpaque) onDraw(canvas); //绘制视图

        // Step 4, draw the children
        dispatchDraw(canvas);//绘制内容

        // Step 5,  //利用step2步骤的值绘制渐变框,不看了
        final Paint p = scrollabilityCache.paint;
        final Matrix matrix = scrollabilityCache.matrix;
        final Shader fade = scrollabilityCache.shader;
 

        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas); //绘制滚动条

        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }
    }

5.ViewGroup 绘制子视图 dispatchDraw 内部流程.

     protected void dispatchDraw(Canvas canvas) {
 
        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {//1.判断是否有布局动画标识,有的话就处理.该参数设置在viewgroup中.
layoutAnimation属性,是ViewGroup为所有子视图设置的动画
            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;

            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;
            controller.start();
        }

        int clipSaveCount = 0;//2.处理padding属性,剪切该视图的canvas.
        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);
        }
 
 
 
        for (int i = 0; i < childrenCount; i++) {//3.遍历子视图,调用它的draw方法,此时的canvas的范围经过上一部的剪裁后的
      //就是该视图内可供子视图绘制的区域.
            final View child = (preorderedList == null)
                    ? children[childIndex] : preorderedList.get(childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);//此时还会调用到view的draw方法,但是是三个参数的.和之前的不同.
            }
        }
      

      //4.绘制被该视图remove但是扔在执行移除动画的子视图.
        if (mDisappearingChildren != null) {
            final ArrayList disappearingChildren = mDisappearingChildren;
            final int disappearingCount = disappearingChildren.size() - 1;
          
            for (int i = disappearingCount; i >= 0; i--) {
                final View child = disappearingChildren.get(i);
                more |= drawChild(canvas, child, drawingTime);
            }
        }
 
      //5.通知重绘界面
        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
            invalidate(true);
        }
        //6.通知布局动画绘制完成
        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
                mLayoutAnimationController.isDone() && !more) {
            final Runnable end = new Runnable() {
               public void run() {
                   notifyAnimationListener();
               }
            };
            post(end);
        }
    }

6. View.draw(Canvas canvas, ViewGroup parent, long drawingTime)

这个方法是通过ViewGroup.drawChild()调用的,不建议被重写.核心过程就是为该视图分配合理的canvas剪裁区

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

        final Animation a = getAnimation();//1.有动画就执行动画.没动画清除掉view变化的值.
        if (a != null) {
            more = drawAnimation(parent, drawingTime, a, scalingRequired);
            concatMatrix = a.willChangeTransformationMatrix();
            if (concatMatrix) {
                mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            transformToApply = parent.getChildTransformation();
        }  

        // 2.判断子视图的canvas剪裁区是否超出父视图的剪切区,如果超过.就不绘制.
        if (!concatMatrix &&
                (flags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
                        ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN &&
                canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
                (mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
            mPrivateFlags2 |= PFLAG2_VIEW_QUICK_REJECTED;
            return more;
        }
 
        if (!hasDisplayList) {
            computeScroll(); //3.计算视图的当前滚动值
            sx = mScrollX;
            sy = mScrollY;
        }

 
        if (offsetForScroll) {//3.通过计算的滚动值,将canvas的坐标原点由父视图的坐标原点改为子视图的左边原点
      //注意.一个视图的坐标远点不总是他的左上角.因为如果视图的内容可以滚动,导致部分内容不可见.这种视图的坐标远点其实是他内容的左上角,
            canvas.translate(mLeft - sx, mTop - sy);
        } else {
            if (!usingRenderNodeProperties) {
                canvas.translate(mLeft, mTop);
            }
            if (scalingRequired) {
                if (usingRenderNodeProperties) {
                    restoreTo = canvas.save();
                }
                // mAttachInfo cannot be null, otherwise scalingRequired == false
                final float scale = 1.0f / mAttachInfo.mApplicationScale;
                canvas.scale(scale, scale);
            }
        }
     
        //4.剪裁canvas.使他匹配当前视图的.
        if (!usingRenderNodeProperties) {
            if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) == ViewGroup.FLAG_CLIP_CHILDREN
                    && cache == null) {
                if (offsetForScroll) {
                    canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop));
                } else {
                    if (!scalingRequired || cache == null) {
                        canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop);
                    } else {
                        canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
                    }
                }
            }
 
        }

          //5.绘制视图.这里分有缓存和没缓存的处理, -代码不看了.
            if (!layerRendered) {
                if (!hasDisplayList) {
    
                    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
                        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                        dispatchDraw(canvas);
                        if (mOverlay != null && !mOverlay.isEmpty()) {
                            mOverlay.getOverlayView().draw(canvas);
                        }
                    } else {
                        draw(canvas);
                    }
                    drawAccessibilityFocus(canvas);
                } else {
                    mPrivateFlags &= ~PFLAG_DIRTY_MASK;
                    ((HardwareCanvas) canvas).drawRenderNode(renderNode, null, flags);
                }
            }
        }   
        return more;
    }
3.png

7.关于canvas的剪切.简单来讲就是理解这个图, ①是父视图的布局大小以及父视图具有的Canvas剪切大小. 子视图1表示子视图1的布局大小. 而②标识子视图1的内容大小.剪裁的过程就是把canvas从①剪裁到②,然后交给子视图1.而此时对于子视图1,他的canvas坐标远点是起内容的左上角,既数字②的位置.

你可能感兴趣的:(view -draw过程)