<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      安卓筆記俠

      專注安卓開發

      導航

      Android View體系(七)從源碼解析View的measure流程

      前言

      在上一篇我們了解了Activity的構成后,開始了解一下View的工作流程,就是measure、layout和draw。measure用來測量View的寬高,layout用來確定View的位置,draw則用來繪制View。這一講我們來看看measure流程,measure流程分為View的measure流程和ViewGroup的measure流程,只不過ViewGroup的measure流程除了要完成自己的測量還要遍歷去調用子元素的measure()方法。

      1.View的measure流程

      先來看看onMeasure()方法(View.java):

      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
           setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                   getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
       }
      View Code

      看看setMeasuredDimension()方法:

      protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
             boolean optical = isLayoutModeOptical(this);
             if (optical != isLayoutModeOptical(mParent)) {
                 Insets insets = getOpticalInsets();
                 int opticalWidth  = insets.left + insets.right;
                 int opticalHeight = insets.top  + insets.bottom;
      
                 measuredWidth  += optical ? opticalWidth  : -opticalWidth;
                 measuredHeight += optical ? opticalHeight : -opticalHeight;
             }
             setMeasuredDimensionRaw(measuredWidth, measuredHeight);
         }
      View Code

      很顯然是用來設置View的寬高的,先來看看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;
      }
      View Code

      specMode是View的測量模式,而specSize是View的測量大小,看到這里我們有必要先看看MeasureSpec類:

      public static class MeasureSpec {
              private static final int MODE_SHIFT = 30;
              private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
      
              /**
               * Measure specification mode: The parent has not imposed any constraint
               * on the child. It can be whatever size it wants.
               */
              public static final int UNSPECIFIED = 0 << MODE_SHIFT;
      
              /**
               * Measure specification mode: The parent has determined an exact size
               * for the child. The child is going to be given those bounds regardless
               * of how big it wants to be.
               */
              public static final int EXACTLY     = 1 << MODE_SHIFT;
      
              /**
               * Measure specification mode: The child can be as large as it wants up
               * to the specified size.
               */
              public static final int AT_MOST     = 2 << MODE_SHIFT;
      
      ...
      
       public static int getMode(int measureSpec) {
                  return (measureSpec & MODE_MASK);
              }
        public static int getSize(int measureSpec) {
                  return (measureSpec & ~MODE_MASK);
              }
      ...        
      }
      View Code

      MeasureSpec類幫助我們來測量View,它是一個32位的int值,高兩位為specMode (測量的模式),低30位為specSize (測量的大小),測量模式分為三種:

      • UNSPECIFIED:未指定模式,View想多大就多大,父容器不做限制,一般用于系統內部的測量。
      • AT_MOST:最大模式,對應于wrap_comtent屬性,只要尺寸不超過父控件允許的最大尺寸就行。
      • EXACTLY:精確模式,對應于match_parent屬性和具體的數值,父容器測量出View所需要的大小,也就是specSize的值。

      讓我們回頭看看getDefaultSize()方法,很顯然在AT_MOST和EXACTLY模式下,都返回specSize這個值,也就是View測量后的大小,而在UNSPECIFIED模式返回的是getDefaultSize()方法的第一次個參數的值,這第一個參數從onMeasure()方法來看是getSuggestedMinimumWidth()方法和getSuggestedMinimumHeight()得到的,那我們來看看getSuggestedMinimumWidth()方法做了什么,我們只需要弄懂getSuggestedMinimumWidth()方法,因為這兩個方法原理是一樣的:

      protected int getSuggestedMinimumWidth() {
            return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
        }
      View Code

      如果View沒有設置背景則取值為mMinWidth,mMinWidth是可以設置的,它對應于android:minWidth這個屬性設置的值或者View的setMinimumWidth的值,如果不指定的話則默認為0:

      public void setMinimumWidth(int minWidth) {
          mMinWidth = minWidth;
          requestLayout();
      }
      View Code

      如果View設置了背景在取值為max(mMinWidth, mBackground.getMinimumWidth()),取值mMinWidth和mBackground.getMinimumWidth()的最大值,上面我們說過了mMinWidth,那來看看mBackground.getMinimumWidth(),這個mBackground是Drawable類型的,看一下Drawable類的getMinimumWidth()方法(Drawable.java):

      public int getMinimumWidth() {
             final int intrinsicWidth = getIntrinsicWidth();
             return intrinsicWidth > 0 ? intrinsicWidth : 0;
      }
      View Code

      intrinsicWidth得到的是這個Drawable的固有的寬度,如果固有寬度大于0則返回固有寬度,否則返回0。
      總結一下getSuggestedMinimumWidth()方法就是:如果View沒有設置背景則返回mMinWidth ,如果設置了背景就返回mMinWidth 和Drawable最小寬度兩個值的最大值。

      2.ViewGroup的measure流程

      ViewGroup的measure原理

      講完了View的measure流程,接下來看看ViewGroup的measure流程,對于ViewGroup,它不只要measure自己本身,還要遍歷的調用子元素的measure()方法,ViewGroup中沒有定義onMeasure()方法,但他定義了measureChildren()方法(ViewGroup.java):

      protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
             final int size = mChildrenCount;
             final View[] children = mChildren;
             for (int i = 0; i < size; ++i) {
                 final View child = children[i];
                 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
                     measureChild(child, widthMeasureSpec, heightMeasureSpec);
                 }
             }
         }
      View Code

      就是遍歷子元素并調用measureChild()方法:

      protected void measureChild(View child, int parentWidthMeasureSpec,
                 int parentHeightMeasureSpec) {
             final LayoutParams lp = child.getLayoutParams();
      
             final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                     mPaddingLeft + mPaddingRight, lp.width);
             final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                     mPaddingTop + mPaddingBottom, lp.height);
      
             child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
         }
      View Code

      調用child.getLayoutParams()方法來獲得子元素的LayoutParams屬性,并獲取到子元素的MeasureSpec并調用子元素的measure()方法進行測量。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);
      }
      View Code

      很顯然這是根據父容器的MeasureSpec的模式再結合子元素的LayoutParams屬性來得出子元素的MeasureSpec屬性,有一點需要注意的是如果父容器的MeasureSpec屬性為AT_MOST,子元素的LayoutParams屬性為WRAP_CONTENT,那根據代碼我們會發現子元素的MeasureSpec屬性也為AT_MOST,它的specSize值為父容器的specSize減去padding的值,也就是說跟這個子元素設置LayoutParams屬性為MATCH_PARENT效果是一樣的,為了解決這個問題需要在LayoutParams屬性為WRAP_CONTENT時指定一下默認的寬和高。

      LinearLayout的measure流程

      ViewGroup并沒有提供onMeasure()方法,而是讓其子類來各自實現測量的方法,究其原因就是ViewGroup有不同的布局的需要很難統一,接下來我們來簡單分析一下ViewGroup的子類LinearLayout的measure流程,先來看看它的onMeasure()方法(LinearLayout.java):

      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
             if (mOrientation == VERTICAL) {
                 measureVertical(widthMeasureSpec, heightMeasureSpec);
             } else {
                 measureHorizontal(widthMeasureSpec, heightMeasureSpec);
             }
         }
      View Code

      來看看垂直measureVertical()方法的部分源碼:

      void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
              mTotalLength = 0;
           mTotalLength = 0;       
       ...
        for (int i = 0; i < count; ++i) {
                  final View child = getVirtualChildAt(i);
      
                  if (child == null) {
                      mTotalLength += measureNullChild(i);
                      continue;
                  }
      
                  if (child.getVisibility() == View.GONE) {
                     i += getChildrenSkipCount(child, i);
                     continue;
                  }
      
                  if (hasDividerBeforeChildAt(i)) {
                      mTotalLength += mDividerHeight;
                  }
      
                  LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
      
                  totalWeight += lp.weight;
                  
                  if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
                      // Optimization: don't bother measuring children who are going to use
                      // leftover space. These views will get measured again down below if
                      // there is any leftover space.
                      final int totalLength = mTotalLength;
                      mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                      skippedMeasure = true;
                  } else {
                      int oldHeight = Integer.MIN_VALUE;
      
                      if (lp.height == 0 && lp.weight > 0) {
                          // heightMode is either UNSPECIFIED or AT_MOST, and this
                          // child wanted to stretch to fill available space.
                          // Translate that to WRAP_CONTENT so that it does not end up
                          // with a height of 0
                          oldHeight = 0;
                          lp.height = LayoutParams.WRAP_CONTENT;
                      }
      
                      // Determine how big this child would like to be. If this or
                      // previous children have given a weight, then we allow it to
                      // use all available space (and we will shrink things later
                      // if needed).
                      measureChildBeforeLayout(
                             child, i, widthMeasureSpec, 0, heightMeasureSpec,
                             totalWeight == 0 ? mTotalLength : 0);
      
                      if (oldHeight != Integer.MIN_VALUE) {
                         lp.height = oldHeight;
                      }
      
                      final int childHeight = child.getMeasuredHeight();
                      final int totalLength = mTotalLength;
                      mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                             lp.bottomMargin + getNextLocationOffset(child));
      ...
      
              if (useLargestChild &&
                      (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
                  mTotalLength = 0;
      
                  for (int i = 0; i < count; ++i) {
                      final View child = getVirtualChildAt(i);
      
                      if (child == null) {
                          mTotalLength += measureNullChild(i);
                          continue;
                      }
      
                      if (child.getVisibility() == GONE) {
                          i += getChildrenSkipCount(child, i);
                          continue;
                      }
      
                      final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                              child.getLayoutParams();
                      // Account for negative margins
                      final int totalLength = mTotalLength;
                      mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
                              lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
                  }
              }
      
              // Add in our padding
              mTotalLength += mPaddingTop + mPaddingBottom;
      
              int heightSize = mTotalLength;
      
              // Check against our minimum height
      View Code

      定義了mTotalLength用來存儲LinearLayout在垂直方向的高度,然后遍歷子元素,根據子元素的MeasureSpec模式分別計算每個子元素的高度,如果是wrap_content則將每個子元素的高度和margin垂直高度等值相加并賦值給mTotalLength得出整個LinearLayout的高度。如果布局高度設置為match_parent者具體數值則和View的測量方法一樣。

      posted on 2016-11-06 18:18  安卓筆記俠  閱讀(419)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 国产成人精品一区二区三| 亚洲人成网网址在线看| 人妻av资源先锋影音av资源| 动漫av网站免费观看| 手机无码人妻一区二区三区免费| 日本一区不卡高清更新二区| 亚洲国产精品成人综合色在| 国产精品中文字幕视频| 人人妻人人做人人爽夜欢视频| 亚洲色一色噜一噜噜噜| 国内精品久久久久影院日本| 国产尤物精品自在拍视频首页| 亚洲精品久久国产高清| 日韩中文日韩中文字幕亚| 久热99热这里只有精品| 97中文字幕在线观看| 亚洲欧美综合人成在线| 国产一区二区不卡视频在线| 亚洲精品一区二区动漫| 欧美寡妇xxxx黑人猛交| 国产亚洲精品精品精品| 午夜通通国产精品福利| 国产成人高清亚洲综合| 国产精品福利自产拍在线观看| 国产中文字幕一区二区| 久久久久人妻一区二区三区| 亚洲偷自拍国综合| 四虎永久免费高清视频| 一区二区三区精品视频免费播放| 国产蜜臀在线一区二区三区 | 午夜天堂av天堂久久久| 越南毛茸茸的少妇| 久久精品亚洲国产综合色| 欧美成人精品手机在线| 香蕉EEWW99国产精选免费| 99久久精品国产亚洲精品| 欧美牲交a欧美牲交aⅴ图片| 国产乱理伦片在线观看| 亚洲丰满熟女一区二区蜜桃| 国产自产视频一区二区三区| 午夜成人精品福利网站在线观看|