自定义view之view的工作流程

来源:互联网 发布:超人打水软件 编辑:程序博客网 时间:2024/05/16 18:06

自定义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的工作流程。
请大家跟上一起进步,一起告别码农!!!!!!!!






0 0