view 绘制流程

2017-09-05  本文已影响0人  刘佳阔
performTravseral.png 3.png
2.png

-接着看vp如何测设计子类的MeasureSpace

 //parentWidthMeasureSpec,parentHeightMeasureSpec 是vp自己的measureSpace宽高值
    //widthUsed,heightUsed 是被vp额外用掉的宽高值
    protected void measureChildWithMargins(View child,
        int parentWidthMeasureSpec, int widthUsed,
        int parentHeightMeasureSpec, int heightUsed) {

        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        
        //viewgroup自己的measureSpace,第二个参数是vp所使用的值
        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);
        //调用子类进行测量
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
    //得到子类的MeasureSpace 通过父类的specMode和子类Layoutparams的组合得到子类最终的MeasureSpace
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        vp的MeasureSpec
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
        
        //vp自己的尺寸减去自己的padding.margin等 最后得到子类能用的最大尺寸
        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: //vp是精确模式
            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);
     }

-最终的匹配结果如下,parentSize值最终父类可用大小,既getChildMeasureSpec中的 size

4.png

-measure 过程

-view 的measures过程

view 的measure方法是final,既子类无法重写该方法,在measure 中又调用了onMeasure方法,只需要看onMeasure方法就可以.

//widthMeasureSpec,heightMeasureSpec为父类vp传过来的子类的MeasureSpace 宽高值
//getSuggestedMinimumWidth()  是建议的最小宽度,一般为view背景的大小和属性android:minWidth大小的最大值.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
//子类可以重写这个方法来决定view的measure大小.但是必须调用setMeasuredDimension()方法,不然会抛出异常.
setMeasuredDimension()方法会设置view宽高的测量值.

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;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result;
}
//UNSPECIFIED的情况可以忽略.看剩下两种情况.所以 getDefaultSize的取值其实就是vp传过来的measureSpace的specSize的值.
5.png

-ViewGroup 的measures过程

-layout 过程

-vieGroup layout过程

public final void layout(int l, int t, int r, int b) {
        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
            if (mTransition != null) {
                mTransition.layoutChange(this);
            }
            super.layout(l, t, r, b); //其实还是调用了父类,最终调用view 的layout
        } else {
            // record the fact that we noop'd it; request layout when transition finishes
            mLayoutCalledWhileSuppressed = true;
        }
}

-draw 过程

1、对View的背景进行绘制
2、保存当前的图层信息(可跳过)
3、绘制View的内容
4、对View的子View进行绘制(如果有子View)
5、绘制View的褪色的边缘,类似于阴影效果(可跳过)
6、绘制View的装饰(例如:滚动条)

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) {
  drawBackground(canvas); //绘制背景
 }
 
 // skip step 2 & 5 if possible (common case)
 final int viewFlags = mViewFlags;
 boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
 boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
 if (!verticalEdges && !horizontalEdges) {
  // Step 3, draw the content
  if (!dirtyOpaque) onDraw(canvas); //空实现,需要子类实现
 
  // Step 4, draw the children
  dispatchDraw(canvas);
 
  // Overlay is part of the content and draws beneath Foreground
  if (mOverlay != null && !mOverlay.isEmpty()) {
   mOverlay.getOverlayView().dispatchDraw(canvas);
  }
 
  // Step 6, draw decorations (foreground, scrollbars)
  onDrawForeground(canvas);
 
  // we're done...
  return;
 }
 ...
}


分布讲解
1绘制背景
private void drawBackground(Canvas canvas) {

     //mBackground是该View的背景参数,比如背景颜色,没有背景就不绘制
     final Drawable background = mBackground;
     if (background == null) {
      return;
     }
     
     //根据View四个布局参数来确定背景的边界 mBackground.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
     setBackgroundBounds();
     
     ...
     
     //获取当前View的mScrollX和mScrollY值
     final int scrollX = mScrollX;
     final int scrollY = mScrollY;
     if ((scrollX | scrollY) == 0) {
      background.draw(canvas);
     } else {
      //如果scrollX和scrollY有值,则对canvas的坐标进行偏移,再绘制背景
      canvas.translate(scrollX, scrollY);
      background.draw(canvas);
      canvas.translate(-scrollX, -scrollY);
     }
}
    3.绘制子View,view中这个方法为空,viewgroup实现了这个方法
    protected void dispatchDraw(Canvas canvas) {
        boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
        final int childrenCount = mChildrenCount;
        final View[] children = mChildren;
        int flags = mGroupFlags;
            
        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;
                }
            }
            int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
            final View child = (preorderedList == null)? children[childIndex] : preorderedList.get(childIndex);
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                more |= drawChild(canvas, child, drawingTime);//这句是重点
            }
        }
            //省略...
    }

    drawChild(canvas, transientChild, drawingTime)的实现,调用了子view的绘制方法
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         return child.draw(canvas, this, drawingTime);
    }

自定义view,宽高写为wrap_content时,如果不处理,和写成 march_parent是一样的,处理规则如下,mWidth,mHeigh为自定义宽高

11.png
上一篇下一篇

猜你喜欢

热点阅读