自定义View(二)

自定义view有三个重要的方法,onMeasure,onLayout,onDraw。今天先从onMeasure开始。


View层次

首先,先从最简单的看起。我们最常用到的设置布局就是

setContentView(R.layout.activity_main);```

  在Activity中找到它的源码:

public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

public Window getWindow() {

return mWindow;

}```
所以可以看出调用了Window里的setContentView方法。而Window类是一个抽象类,其唯一实现类是PhoneWindow,看看PhoneWindow类的setContentView方法。

    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }```

首先判断mContentParent是否为空,mContentParent是一个ViewGroup对象,即判定是否第一次调setContentView方法,如果是就调用installDecor方法,如果不是就清除ViewGroup里的View。

private void installDecor() {
//只展示核心代码
    if (mDecor == null) {
        mDecor = generateDecor();
        mDecor.setIsRootNamespace(true);
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
}
}
 

protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
}
}```

mDecor是DecorView的一个对象,DecorView是PhoneWindow里的一个内部类。首先判断mDecor是否为空,如果为空就创建一个DecorView对象,接下来判断mContentParent是否为空,如果是,调用generateLayout。

protected ViewGroup generateLayout(DecorView decor)
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        return contentParent;
    }

contentParent和mDecor创建完毕后,通过PhoneWindow里的setContentView的mLayoutInflater.inflate(layoutResID,mContentParent)把xml文件传递到屏幕。

通过Hierarchy View得到的View层次结构:


自定义View(二)_第1张图片
Hierarchy View

整个屏幕是一个FrameLayout,里面包含一个LinearLayout和两个View,分别是statusBar和nagavitionBar,状态栏和导航栏,LinearLayout里是一个FrameLayout,FrameLayout里又嵌套一个ViewGroup(普遍当成是LinearLayout),ViewGroup里有两个FrameLayout,一个是content,一个是title。


自定义View(二)_第2张图片
view

至此view的层次介绍完毕。接下来看一下自定义View的onMeasure方法。


onMeasure

    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    }

重写onMeasure方法,直接调用super.onMeasure()调用父类方法,或者直接调用setMeasuredDimension设置view的宽高。里面有两个很重要的参数,widthMeasureSpec和heightMeasureSpec,这两个参数。至于这两个参数的来源,就要找到view的绘制方法。view的绘制是在ViewRootImpl的performTraversals方法实现的

    private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;
        int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
        int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
        performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    
    }
   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;
    }
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);
        }
    }

getRootMeasureSpec传入两个参数,mWidth和lp.width,lp.width和lp.height的值为MATCH_PARENT。然后把得到的measureSpec传给performMeasure,performMeasure调用view的measure方法,measure方法又调用onMeasure方法,这样就完成了widthMeasureSpec和heightMeasureSpec从ViewRootImpl到onMeasure的传递。

在这里用到了一个特殊的类MeasureSpec。

    public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }
    public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
    public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

MeasureSpec中mode为高两位,size为低30位。MODE_MASK为11 000...(30个0),&的规则是遇0变0,遇1不变。所以getMode就取得了高两位,getSize对MODE_MASK取反,变成了00 11111...(30个1)取得低30位。

在onMeasure方法中,调用如下代码:

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
    protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }
    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;
    }

ViewGroup没有重写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);
            }
        }
    }
    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);
    }

可以看到,measureChildren方法遍历view并调用measureChild方法,measureChild方法根据父类的MeasureSpec和自身的LayoutParam构成自身的measureSpec并调用自身的measure方法测量。这就是我们常说的view的大小是由父类的大小和自身共同决定的。

你可能感兴趣的:(自定义View(二))