View的工作原理

View工作原理包括其三大流程:onMeasure() -->onLayout--->onDraw()
测量、布局、绘制。
View的常用回调方法onAttach() onVisibilityChanged() onDetach()
如果View是可以滑动的,还需要解决View的滑动冲突
View 的自定义实现方式
1.继承View/viewGroup
2.继承现有的控件

 ### 初识ViewRoot 和DecorView    

ViewRoot对应于ViewRootImpl是最终执行onMeasure() --onLayout() --onDraw()的类,
连接了WinodowManager和DecorView;
DecorView作为顶级View,其实是一个FrameLayout,通常包含一个Vertical的LinearLayout,里面是 titlebar +contentView,我们用的setContentView()就是set到这个content里。
所有的View事件都是经过Decorview然后传到我们的View.

Measure 过程

view的measure:

   ### OnMeasure

View系统绘制从ViewRoot.performTraversals() 开始,在其内部调用
View系统的绘制流程会从ViewRoot的performTraversals()方法中开始,在其内部调用View的measure()方法。measure()方法接收两个参数,

接下来我们看下View的measure()方法里面的代码吧,如下所示:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
            widthMeasureSpec != mOldWidthMeasureSpec ||
            heightMeasureSpec != mOldHeightMeasureSpec) {
        mPrivateFlags &= ~MEASURED_DIMENSION_SET;
        if (ViewDebug.TRACE_HIERARCHY) {
            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
        }
        onMeasure(widthMeasureSpec, heightMeasureSpec);
        if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
            throw new IllegalStateException("onMeasure() did not set the"
                    + " measured dimension by calling"
                    + " setMeasuredDimension()");
        }
        mPrivateFlags |= LAYOUT_REQUIRED;
    }
    mOldWidthMeasureSpec = widthMeasureSpec;
    mOldHeightMeasureSpec = heightMeasureSpec;

如上面代码可以看到:measure()这个方法是final的,因此我们无法在子类中去重写这个方法,说明Android是不允许我们改变View的measure框架的。然后调用了onMeasure()方法,这里才是真正去测量并设置View大小的地方,默认会调用getDefaultSize()方法来获取视图的大小,如下所示:

 ### onMeasure()方法如下:
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

### getDefaultSize()方法如下:
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;
}

这上上面的代码中传入的measureSpec是measure()方法中传来的,先解析measureSpec中的specSize specMode,
如果是AT_MOST EXACTLY,就返回specSize(),这是系统默认的。如果是UNSPECIFIED,测量大小就是getSuggestedMinimumWidth() ;

getSuggestedMinimumWidth() ;

protected int getSuggestedMinimumWidth() {
    //如果没有给View设置背景,那么就返回View本身的最小宽度mMinWidth
    //如果给View设置了背景,那么就取View本身最小宽度mMinWidth和背景的最小宽度的最大值
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

mBackground.getMinimumWidth()返回的是Drawable 的原始宽度,ShapDrawable无原始宽度,BitmapDrawable有原始宽度。即:无背景,返回minWidth(),不设置为0;有背景返回max(mMinWidth,背景最小宽度);
之后在setMeasuredDimension()中设置测量的大小,这样一次measure过程结束。

viewgroup的measure过程

viewgroup除了measure本身,还要遍历子view,调用每个子View的measure()
ViewGroup 是个抽象类,它没有ovveridde View的onMeasure(),
但是定义了measureChildren()来测量子视图大小。

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < size; ++i) {
        final View child = children[i];
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
}

遍历所有子视图,然后逐一调用measureChild() 方法测量子视图。

### measureChild() 方法below :

protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

根据布局中的MATCH_PARENT、WRAP_CONTENT通过getChildMeasureSpec()计算得到 child的MeasureSpec,然后传入measure()中,之后,就是前面的measure()流程。

可以重写 onMeasure()方法:

public class MyView extends View {
 
    ......
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(200, 200);
    }

主要代码:

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    。。。

    
                // Determine how big this child would like to be. If this or
                // previous children have given a weight, then we allow it to
                // use all available space (and we will shrink things later
                // if needed).
                final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
                measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                        heightMeasureSpec, usedHeight);

              
     ....
    

      setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);

    }

measureChildBeforeLayout()内部调用各个子View的measure(),mTotalLength存储Linearlayout在竖直方向的高度,每测量一个view,mTotalLength增加,增加部分是子元素的高度以及竖直的margin;当子元素测量完后,linearlayout进入自己的测量
setMeasuredDimension();

这样不管xml布局怎样设置,view的大小都是200 *200 ;
注意:只有在setMeasuredDimension之后,才可以getMeasuredWidth()来获取测量后的宽高。

ViewGroup没有实现具体的测量方法,因为ViewGroup是抽象类,其测量过程的onMeasure()x需要各个子类具体去实现。为什么需要具体实现?因为不同的ViewGroup子类如RelativeLayout、LinearLayout的特性不一致,所以需要具体实现。
下面以LinearLayout 的onMeasure()为例子,来分析:

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

视图大小由父视图、布局、视图本身决定,父视图提供参考,布局

onLayout() :

   public void layout(int l, int t, int r, int b) {
        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        }

        int oldL = mLeft;
        int oldT = mTop;
        int oldB = mBottom;
        int oldR = mRight;

        boolean changed = isLayoutModeOptical(mParent) ?
                setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

        if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
            onLayout(changed, l, t, r, b);

            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 listenersCopy =
                        (ArrayList)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;

        if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
            mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
            notifyEnterOrExitForAutoFillIfNeeded(true);
        }
    }

这里判断view的位置和尺寸是否改变了,

//如果isLayoutModeOptical()返回true,那么就会执行setOpticalFrame()方法,
//否则会执行setFrame()方法。并且setOpticalFrame()内部会调用setFrame(),
//所以无论如何都会执行setFrame()方法。
//setFrame()方法会将View新的left、top、right、bottom存储到View的成员变量中
//并且返回一个boolean值,如果返回true表示View的位置或尺寸发生了变化,
//否则表示未发生变化
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
如果

你可能感兴趣的:(View的工作原理)