自定义view之view的工作流程

自定义view是我们开发中经常遇到的问题,总结下来之后发现,对于自定义view来说,了解他的工作流程对我们自定义控件的时候有很大的帮助。

1、我们先来看看viewroot和decorview

viewroot对应于viewrootimpl类,它是链接windowmanager和decorview的纽带,view的三大流程均是通过viewroot来完成的。view的绘制流程就是从viewroot的performtraversals方法开始的,它通过measure、layout,draw三个方法,最终才绘制出view的。measure用来测量view的宽和高,layout用来确定view在父容器之中的位置,draw是负责将view绘制在频幕上。大致流程如下:

                                      

measure过程决定了控件的高和宽,当measure过程完成后,可以通过getmeasurewidth和getmeasureheight来获取view的高和宽,在几乎所有的情况下,他都等于view的实际高和宽。layout决定里四个顶点的坐标和实际view的宽和高。完成后可以通过gettop、getleft、getright、getleft、getbottom方法来获取。draw方法决定了view的显示。

2、理解measurespec

measurespec代表一个32位的int值,高两位代表SpecMode,第三十为表示specsize。specMode代表测量模式,specSize代表测量规格大小。它将specmode和specsize打包成int值,来避免过多的对象内存分配,specmode有以下几种方式:

UNspecified  父容器不对view有任何限制,要多大给多大,一般用于系统内部,表示一种测量状态;

exactly    父容器已经检测出view所需要的精确值,这时候view的最终大小就是specsize 所指定的值,它对于layoutparams中的match_parent和具体数值这两种模式。

at_most   父容器制定了大小值,view的最大值不能大于父容器所指定的值。适用于layoutparams中的wrap_content;我们不能直接通过制定view的layoutparams来制定view的大小,他必须配合父容器的measurespec来一起使用,对于decorview来说,其measurespec由窗口的尺寸和自身的layoutparams来决定。对于普通view其measurespec有父容器的measurespec和自身的layoutparams来决定。

layoutparams.match_parent 精确模式,大小就是窗口大小;

layoutparams.wrap_content 最大模式,大小不定,但是大小不能超过窗口的大小;

固定大小     精确模式,大小为layoutparams中所指定的大小;

measurespec一旦确定后,就可以通过onmeasure来确定view的宽和高。view 的measure由viewgroup传递而来,先看一下viewgroup的measureChildWithMargins:

  protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

上边的方法对view子元素进行measure方法,在对子元素进行measure方法之前,我们先通过getchildmeasurespec方法获取子元素的measure,通过上边的代码可以看出来子元素的measure和父容器的measure和自身的layoutparams有过,还和view的margin和padding有关。可以看看getchildmeasurespec方法:

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
对于上面的代码,我们不难理解,他是通过父元素的measurespec和子元素自身的layoutparams来确定子元素的measurespec。代码中的padding是指父元素所占用的空间大小。子元素的大小为父元素的大小减去padding。它清楚展示了普通子view的measurespec的创建规则,为了更清晰的理解他的逻辑,这里提供了如下的表格:




在下一节我们将继续讲解view的工作流程。
请大家跟上一起进步,一起告别码农!!!!!!!!






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值