Android控件架构
Android中控件被大致分类两类:
- View
- ViewGroup ViewGroup作为父控件可以包含多个View,并管理其中的View控件
通过ViewGroup,整个界面上的控件形成了一个树形的结构,这也就是我们常说的控件树,上层控制着下层控件的测量与绘制,并传递交互事件。
在Activty中使用findViewById()的方法来查找控件,就是在这个控件树上进行深度优先遍历来查找相应的元素的。
每个Activity都包含一个Window对象,在Android中通过由phoneWindow来实现。PhoneWindow将一个DoctorView设置为整个应用窗口的根View。DoctorView作为窗口界面的顶层视图,封装了一些窗口操作的通用方法。窗口的所有的点击事件都有WindowManagerService来进行接收,并通过Activity对象来回调相应的onClickListener.UI界面的结构的包含关系如下所示:
Acitvity -> Window(PhoneWindow) -> DoctorView -> TitileView and ContentView
在程序的代码中,我们在onCreate()函数中调用setContentView()方法后, ActivitymanagerSrvice会回调onResume()方法,此时系统才会把DoctorView添加到PhoneWindow中。
View的测量onMeasure()
在View绘制之前,我们要对View进行测量,测量是在View的onMeasure()方法中进行的。onMeasure()方法提供参数MeasureSpec,这是一个32位的int值,其中前两位表示测量的模式,后30位表示测量的大小。
Android的测量模式有三种:
- EXACTLY, 即精确模式,在控件中指定了大小或者指定为match_parent时,使用的是这个模式。(默认的模式,如果自定义控件不重写onMeasure()方法,则只能用EXACTLY模式)
- AT_MOST, 即最大值,随着子空间的变化而变化的时候,如wrapcontent
- UNSPECIFIED, 不指定模式, View想多大就多大
我们分析ListView的onMeasure()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//长度和宽度分别制定一个measureSpec
// Sets up mListPadding
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);//measureSpec的前两位为测量模式
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);//measureSpec的后30位位测量值
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int childWidth = 0;
int childHeight = 0;
int childState = 0;
mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
|| heightMode == MeasureSpec.UNSPECIFIED)) {
final View child = obtainView(0, mIsScrap); //得到子View
// Lay out child directly against the parent measure spec so that
// we can obtain exected minimum width and height.
measureScrapChild(child, 0, widthMeasureSpec, heightSize); //递归测量子View的长度和宽度
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(
((LayoutParams) child.getLayoutParams()).viewType)) {
mRecycler.addScrapView(child, 0);
}
}
if (widthMode == MeasureSpec.UNSPECIFIED) { //如果制定的模式为UNSPECIFIED, 则想多大就多大, 大小为子空间的大小+左右padding等
widthSize = mListPadding.left + mListPadding.right + childWidth +
getVerticalScrollbarWidth();
} else {
widthSize |= (childState & MEASURED_STATE_MASK);
}
if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = mListPadding.top + mListPadding.bottom + childHeight +
getVerticalFadingEdgeLength() * 2;
}
if (heightMode == MeasureSpec.AT_MOST) {
// TODO: after first layout we should maybe start at the first visible position, not 0
heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
}
setMeasuredDimension(widthSize, heightSize);
mWidthMeasureSpec = widthMeasureSpec;
}