一、View源码分析
ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程全是通过ViewRoot来完成的。
在ActivityThread中,当Activity对象被创建后,会将DectorView添加到Window中,同时会创建ViewRootImpl对象,并将
ViewRootImpl对象和decorView建立关联,这个过程我们可以参看源码:
root = new ViewRootImpl(view.getContext(),display);root.setView(view,wparams,panelparentView);
View的绘制流程是从ViewRoot的performTeaversals方法开始的,它经过measure、layout、draw三个过程才能最终将一个View绘制出来。
其中measure用来测量View的宽和高,layout用来确定View在父容器中放置的位置,而draw负责将View绘制在屏幕上。
针对performTraversals的大致流程:
如图所示,performTraversals会依次调用performMeasure、performLayout和performDraw三个方法,分别完成顶级View的measure、layout和draw三大流程。
PperformMeasure会调用measure方法,measure方法又会调用onMeasure,其中onMeasure又会调用所有子元素的measure方法完成测量。
同理,performLayout和performDraw也是如此。
DecorView是顶级View,它其实是一个FrameLayout,View层的事件都先经过DecorView,然后才传递给我们的View。
1.1、Measure的原理
measure过程中,如果是View,则通过measure方法即可完成测量过程,如果是ViewGroup,则需要完成自己的测量过程外,
还会遍历去调用所有子元素的measure方法,各个子元素再递归去执行这个流程。
1、View的measure过程
View的measure过程由其measure方法完成,measure方法是被final修饰的方法,不能重写。在View的measure方法中会去调用View的onMeasure方法,
因此只需要看onMeasure方法的实现即可:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
setMeasureDimension方法会设置View的宽和高来代表测量的结束,因此我们只需要看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;}
对于我们来说只需要看AT_MOST和EXACTLY两种情况,其实getDefaultSize返回的大小就是measureSpec中的specSize,它就是View测量后的大小。
而UNSPECIFIED这种情况一般用于系统内部的测量过程,在这种情况下,View的大小为getDefault的第一个参数size,即宽和高分别是
getSuggestedMinimumWidth和getSuggestedMinimumHeught这两个方法的返回值。
protected int getSuggestedMinimumHeight() { return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());}protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());}
可以看出,如果在没有指定背景的情况下,宽度和高度分别是mMinWidth和mMinHeight,如果不指定该属性,则默认为0.
如果指定背景的情况下,宽度和高度分别是max(mMinHeight, mBackground.getMinimumHeight())和max(mMinWidth, mBackground.getMinimumWidth())
那么我来看看mBackground.getMinimumWidth()方法:
public int getMinimumWidth() { final int intrinsicWidth = getIntrinsicWidth(); return intrinsicWidth > 0 ? intrinsicWidth : 0;}
可以看出,在Drawable有原始宽度的情况下,该方法返回的就是Drawable的原始宽度,当然高度也是如此。