android-自定义View解决wrap_content无效的问题

来源:互联网 发布:创意个人域名 编辑:程序博客网 时间:2024/06/05 16:09
###问题提出在我们自定义view时,如何需要是当前的view内容自适应,这种平常的使用中,只需要在xml文件中制定宽高或者长高为wrap_content即可,但是如果该view是我们自定义的,那么此时再在xml文件中指定宽高为wrap_content则不能起到内容自适应的效果,并且效果为match_parent。本文即是解决此类问题。###预备知识在讲解该问题之前,我们需要了解一些预备知识,以更加清楚的了解原理。- 自定义view过程中,我们通常需要关注3个过程,即 测量 / 布局 / 绘制 。 本文的问题位于测量过程,所以本文也将着重了解测量过程。- 在测量过程中,其目的是为了得到view的尺寸表示,该表示通过一个封装类为MeasureSpec所标识。测量过程实际上可以说是得到该view的MeasureSpec的过程。- MeasureSpec是通过将一个int(32)的数组成而成的,本质是一个int数,MeasureSpec由两部分组成:SepcMode 和 SpecSize 。其中SpecMode为MeasueSpec的高2位,SpecSize为MeasureSpec的低30位。SpecMode有3类: - UNSPECIFIED:父容器不对View有任何限制,要多大有多大,这种情况一般用于系统内容。在我们使用过程中,一般不考虑这种模式。 - EXACTLY:父容器已经检测到了View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的大小。它对应的LayotParams中的match_parent 和具体的数值。 - AT_MOST: 父容器指定了一个可用大小SpecSize,View的大小不能大于这个值,它对应于LayoutParams中的wrap_content。- View的MeasureSpec与父容器的MeasureSpec和本身的LayoutParams有关,并且由二者所决定,决定规则如下:![输入图片说明](https://static.oschina.net/uploads/img/201602/17191840_emvA.jpg "在这里输入图片标题")###问题出现原理现在我们看一下View的onMeasure方法。该方法实际上就是View测量过程中最重要的方法。``` protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)```对于一个View而言,它的onMeasure方法一般由其父容器所调用,并且传入```widthMeasureSpec```和```heighMeasureSpec```,这两个值就是来源于我们在xml文件中指定的,值为wrap_content / match_parent / 或者具体数值。下面我们具体看一下onMeasure方法的具体实现:```protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }```getDefaultSize:```public static int getDefaultSize(int size, int measureSpec) {        int result = size;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        switch (specMode) {        case MeasureSpec.UNSPECIFIED:            result = size;            break;        case MeasureSpec.AT_MOST:        case MeasureSpec.EXACTLY:            result = specSize;            break;        }        return result;    }```从上面两个方法我们可以知道:在默认实现中,AT_MOST和EXACTLY两种模式都会被设置成specSize。我们也知道AT_MOST对应于wrap_content,而EXACTLY对应于match_parent和具体数值情况。也就说默认情况下wrap_content和match_parent是具有相同的效果的。那有人会问:wrap_content和match_parent具有相同的效果,为什么是填充父容器的效果呢?下面讲一下View的绘制过程:View的绘制首先起于ViewRootImpl,并且View的三个流程也是通过ViewRootImpl来完成的,在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建viewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。之后,View的绘制过程从ViewRootImpl的performTraversals方法开始,它经过mwasure、layout、draw三个过程最终将一个View绘制出来,由于DecorView是Android的一个页面的顶级View,所以绘制过程首先会从DecorView开始,又因为DecorView是一个ViewGroup,它会遍历绘制所有的子View,我们注意到在protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法中会有两个参数,其实这两个参数是在ViewGroup中传入进去的,最初是在DecorView中赋值的,且其值就是屏幕的宽度和高度。###问题解决从上面的分析我们知道:出现这个问题的原因是在测量过程中没有处理wrap_content的情况,所以我们在自定义View的时候,如果在直接继承自View,因为View的默认实现是没有处理这种情况的,所以wrap_content会出现与atch_parent相同的效果,要想解决这个问题,我们就要特殊处理。下面是一种典型的处理方式:```@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    int desiredWidth = 100;    int desiredHeight = 100;    int widthMode = MeasureSpec.getMode(widthMeasureSpec);    int widthSize = MeasureSpec.getSize(widthMeasureSpec);    int heightMode = MeasureSpec.getMode(heightMeasureSpec);    int heightSize = MeasureSpec.getSize(heightMeasureSpec);    int width;    int height;    //Measure Width    if (widthMode == MeasureSpec.EXACTLY) {        //Must be this size        width = widthSize;    } else if (widthMode == MeasureSpec.AT_MOST) {        //Can't be bigger than...        width = Math.min(desiredWidth, widthSize);    } else {        //Be whatever you want        width = desiredWidth;    }    //Measure Height    if (heightMode == MeasureSpec.EXACTLY) {        //Must be this size        height = heightSize;    } else if (heightMode == MeasureSpec.AT_MOST) {        //Can't be bigger than...        height = Math.min(desiredHeight, heightSize);    } else {        //Be whatever you want        height = desiredHeight;    }    //MUST CALL THIS    setMeasuredDimension(width, height);}```解决的方法总结:- 1。指定一个默认的内部宽高,例如本方法中的desiredWidth = 100。- 2。判断当MeasureSpec的模式为AT_MOST(对应于wrap_content)时,设置结果为设置的值(最大为我们设置的值,如果小于设置值就设置为specSize)。- 3。判断当MeasureSpec的模式为非AT_MOST时,直接设置为系统的测量值即可。- 4。设置值没有特定的标准,以实际情况为准。
阅读全文
0 0
原创粉丝点击