• View 的measure 和onMeasure


    最近有人问了我关于measure 和 onMeasure的一些问题,什么时候调用measure方法, 两者的区别,什么时候重写onMeasure方法。其实网上有很多人写过这方面的博客。我觉得不要因为网上有了,就不写。看懂是一回事,讲出来是一回事,写出来又是另外一回事。看了东西还是别人了,只有通过写或是讲出来才能更深刻的理解。

    我们先看下什么时候会调用 measure方法:

    讲到view的绘制原理上,肯定都会提到ViewRootImpl类,该类是activity 的view 树与Window的中间通信者。它里面的mView对象指向的是DecorView。ViewRootImpl类是View绘制过程(测量,布局,绘制)的起点。那么measure方法是怎么进行的呢?

    我们先看下调用顺序:

    通过上面的调用顺序,我们可以看到measure是在什么时候调用,在View里,有个mParent的变量。这个变量其实就是ViewRootImpl. 所以在调用View的requestFitSystemWindows, requestLayout, invalidateChildInParent时候,都会调用measure方法。

    那measure方法到底是干什么用的。其实它主要是用于计算并获取该View的实际大小,也就是mMeasuredWidth和mMeasuredHeight两个值。

    1.首先判断是否有设置为opticalBounds的属性,如果当前的opticalBounds的属性和父类的不一致,就重新计算wdithMeasureSpec和heightMeasureSpec

     boolean optical = isLayoutModeOptical(this);
            if (optical != isLayoutModeOptical(mParent)) {
                Insets insets = getOpticalInsets();
                int oWidth  = insets.left + insets.right;
                int oHeight = insets.top  + insets.bottom;
                widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
                heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
            }

    2. 如果有窗口移动或是reSize的操作,导致的强制刷新,或是view的宽度或是长度有变化时候。如果是不用measure cache,那么需要调用onMeasure方法进行进行view的width和height。如果有measure cache,那么只需要拿到measure cache里的值进行更新就可以了。

     if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
                    widthMeasureSpec != mOldWidthMeasureSpec ||
                    heightMeasureSpec != mOldHeightMeasureSpec) {
    
                // first clears the measured dimension flag
                mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
    
                resolveRtlPropertiesIfNeeded();
    
                int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
                        mMeasureCache.indexOfKey(key);
                if (cacheIndex < 0 || sIgnoreMeasureCache) {
                    // measure ourselves, this should set the measured dimension flag back
                    onMeasure(widthMeasureSpec, heightMeasureSpec);
                    mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
                } else {
                    long value = mMeasureCache.valueAt(cacheIndex);
                    // Casting a long to int drops the high 32 bits, no mask needed
                    setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                    mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
                }
    
                // flag not set, setMeasuredDimension() was not invoked, we raise
                // an exception to warn the developer
                if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
                    throw new IllegalStateException("View with id " + getId() + ": "
                            + getClass().getName() + "#onMeasure() did not set the"
                            + " measured dimension by calling"
                            + " setMeasuredDimension()");
                }
    
                mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
            }

    3.最后保存当前的值到measure cache里和重新记录old值,key值是用 widthMeasureSpec和heightMeasureSpec拼凑的64位数

     // Suppress sign extension for the low bytes
            long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
            if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
    
          // ......省略.... 
            mOldWidthMeasureSpec = widthMeasureSpec;
            mOldHeightMeasureSpec = heightMeasureSpec;
    
            mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                    (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension

     

    View里的onMeasure 做得事情很简单,就是根据spec来计算具体的值。当然了如果是RelativeLayout等ViewGroup里的onMeasure就会相当复杂。

     

    那在上面时候需要重写onMeasure方法呢?

    一般是需要自己去定义高宽规则的时候,比如需要显示一个特定高度的listView。不管规则怎么变,在重新计算高宽后,一定要调用setMeasuredDimension

  • 相关阅读:
    Ajax基本案例详解之load的实现
    Ajax基本案例详解之$.post的实现
    Ajax基本案例详解之$.post的实现
    Ajax基本案例详解之$.get的实现
    Ajax基本案例详解之$.get的实现
    Ajax基本案例详解之$.ajax的实现
    Ajax基本案例详解之$.ajax的实现
    [置顶] 别羡慕别人的舒服,静下心来坚持奋斗!!!
    别羡慕别人的舒服,静下心来坚持奋斗!!!
    Struts文件上传与下载详解_文件的下载
  • 原文地址:https://www.cnblogs.com/StephenWu/p/6263099.html
Copyright © 2020-2023  润新知