Android控件架构

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值