美文网首页
自定义View中onMeasure中测量问题

自定义View中onMeasure中测量问题

作者: lkuo | 来源:发表于2017-11-22 16:23 被阅读98次

自定义View设置控件大小或者处理尺寸时,使用如下代码的话,在父布局为RelativeLayout时,会出现控件尺寸显示不对的问题。

   @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
        int widthType = MeasureSpec.getMode(widthMeasureSpec);
        int heightType = MeasureSpec.getMode(heightMeasureSpec);
        int defaultWidth = 100;
        int defaultHeight = 100;
        width = widthType == MeasureSpec.AT_MOST ? defaultWidth : width;
        height = heightType == MeasureSpec.AT_MOST ? defaultHeight : height;
        width = Math.min(width, height);
        height = Math.min(width, height);
        setMeasuredDimension(width, height);
    }

虽然我知道在自定义View测量过程中,onMeasure被调用了两次。但是没有具体研究过...

搞了半天才发现,出现问题的原因和父布局有关:

  1. 当View的父布局为RelativeLayout时
  • onMeasure第一次调用时测量了widthMeasureSpec,此时heightMeasureSpec是默认的(mode是wrap_content类型)。
  • 第二次调用时测量了heightMeasureSpec,但此时的widthMeasureSpec已经被“污染”了(在上述代码中)。

导致最后无论怎么设置控件大小,始终为defaultValue。

  1. 当View的父布局为LinearLayout时,onMeasure在第一次调用时就同时测量好了widthMeasureSpec和heightMeasureSpec。

  2. 继承自LinearLayout的自定义布局,也是测量两次!!!

所以自定义View测量时需要注意。(当然父布局为LinearLayout可以随便处理啦~)

也可以统一处理为:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        int defaultWidth = 100;
        int defaultHeight = 100;
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(defaultWidth, defaultHeight);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(defaultWidth, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, defaultHeight);
        } else {
            widthSpecSize = Math.min(widthSpecSize, heightSpecSize);
            heightSpecSize = Math.min(widthSpecSize, heightSpecSize);
            setMeasuredDimension(widthSpecSize, heightSpecSize);
        }
    }

这样,就可以同时考虑两种情况,以前开发过程中不仔细,没有出现问题,在此记录一下~~也为其它遇到这种问题的小伙伴们提供些参考。



!!!!!!!!!!! resolveSize !!!!!!!!!!
Android已存在处理上诉问题的方法,即resolveSize(int size, int measureSpec)。
第一个参数可以理解为设置的大小尺寸(固定值,或者getSuggestedMinimumWidth()),第二个参数是xml中的模式和由此模式系统默认的大小。
源码如下:

// sdk 27
public static int resolveSize(int size, int measureSpec) {
    return resolveSizeAndState(size, measureSpec, 0) & MEASURED_SIZE_MASK;
}

public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
        final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);
        final int result;
        switch (specMode) {
            case MeasureSpec.AT_MOST:
               //这块处理与之前sdk的处理不一样了
                if (specSize < size) {
                    result = specSize | MEASURED_STATE_TOO_SMALL;
                } else {
                    result = size;
                }
                break;
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
            case MeasureSpec.UNSPECIFIED:
            default:
                result = size;
        }
        return result | (childMeasuredState & MEASURED_STATE_MASK);
    }

故可以写为:

setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
       resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  • 注意:resolveSize和getDefaultSize有区别的,getDefaultSize将AT_MOST、EXACTLY一起处理。
 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;
    }

比较全面的解释:https://www.jianshu.com/p/ca118d704b5e

相关文章

网友评论

      本文标题:自定义View中onMeasure中测量问题

      本文链接:https://www.haomeiwen.com/subject/jccovxtx.html