Android控件架构

来源:互联网 发布:java开发人员培训机构 编辑:程序博客网 时间:2024/04/27 20:58

Android控件架构

Android中控件被大致分类两类:
- View
- ViewGroup ViewGroup作为父控件可以包含多个View,并管理其中的View控件

image

通过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;    }
0 0