国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android視圖的繪制流程(上) View的測量

2019-12-12 04:01:07
字體:
來源:轉載
供稿:網友

綜述

  View的繪制流程可以分為三大步,它們分別是measure,layout和draw過程。measure表示View的測量過程,用于測量View的寬度和高度;layout用于確定View在父容器的位置;draw則是負責將View繪制到屏幕中。下面主要來看一下View的Measure過程。

測量過程

  View的繪制流程是從ViewRoot的performTraversals方法開始的,ViewRoot對應ViewRootImpl類。ViewRoot在performTraversals中會調用performMeasure方法來進行對根View的測量過程。而在performMeasure方法中又會調用View的measure方法。對于View的measure方法它是一個final類型,也就是說這個measure方法不能被子類重寫。但是在measure方法中調用了onMeasure方法。所以View的子類可以重寫onMeasure方法來實現各自的Measure過程。在這里也就是主要對onMeasure方法進行分析。

MeasureSpec

  MeasureSpec是View類中的一個靜態內部類。一個MeasureSpec封裝了父布局傳遞給子布局的布局要求。每個MeasureSpec都代表著一個高度或寬度的要求。每個MesureSpec都是由specSize和specMode組成,它代表著一個32位的int值,其中高2位代表specSize,低30位代表specMode。
  MeasureSpec的測量模式有三種,下面介紹一下這三種測量模式:

UNSPECIFIED
父容器對子View沒有任何的限制,子View可以是任何的大小。
EXACTLY
父容器為子View大小指定一個具體值,View的最終大小就是specSize。對應View屬性match_parent和具體值。
AT_MOST
子View的大小最大只能是specSize,也就是所子View的大小不能超過specSize。對應View屬性的wrap_content.

  在MeasureSpec中可以通過specSize和specMode并使用makeMeasureSpec方法來創建一個MeasureSpec,還可以通過getMode和getSize來獲取MeasureSpec的specMode和specSize。

View的測量過程

  在上面已經說到,View的Measure過程是由measure方法來完成的,而measure方法通過調用onMeasure方法來完成View的Measure過程。那么就來看一下onMeasure方法。

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

  在View的onMeasure方法中只是調用了setMeasuredDimension方法,setMeasuredDimension方法的作用就是設置View的高和寬的測量值。對于View測量后寬和高的值是通過getDefaultSize方法來獲取的。下面就來一下這個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;}


  對于MeasureSpec的AT_MOST和EXACTLY模式下,直接返回的就是MeasureSpec的specSize,也就是說這個specSize就是View測量后的大小。而對于在UNSPECIFIED模式下,View的測量值則為getDefaultSize方法中的第一個參數size。這個size所對應的寬和高是通過getSuggestedMinimumWidth和getSuggestedMinimumHeight兩個方法獲取的。下面就來看一下這兩個方法。

protected int getSuggestedMinimumHeight() {  return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());}protected int getSuggestedMinimumWidth() {  return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());}

  在這里可以看到對于View寬和高的取值是根據View是否存在背景進行設置的。在這里以View的寬度來進行說明。若是View沒有背景則是View的寬度mMinWidth。對于mMinWidth值得設置可以在XML布局文件中設置minWidth屬性,它的默認值為0。也可以通過調用View的setMinimumWidth()方法其賦值。若是View存在背景的話,則取View本身最小寬度mMinWidth和View背景的最小寬度它們中的最大值。

ViewGroup的測量過程

  對于ViewGroup的Measure過程,ViewGroup處理Measure自己本身的大小,還需要遍歷子View,并調用它們的measure方法,然后各個子元素再去遞歸執行Measure過程。在ViewGroup中并沒有重寫onMeasure方法,因為ViewGroup它是一個抽象類,對于不同的具體ViewGroup它的onMeasure方法中所實現的過程不一樣。但是在ViewGroup中提供了一個measureChildren方法,對子View進行測量。下面就來看一下這個measureChildren方法。

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);    }  }}

  在這里獲取ViewGroup中所有的子View。然后遍歷ViewGroup中子View并調用measureChild方法來完成對子View的測量。下面看一下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);}


  在這段代碼中通過getChildMeasureSpec方法獲取子View寬和高的MeasureSpec。然后調用子View的measure方法開始對View進行測量。下面就來看一下是如何通過getChildMeasureSpec方法來獲取View的MeasureSpec的。

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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;      resultMode = MeasureSpec.UNSPECIFIED;    } else if (childDimension == LayoutParams.WRAP_CONTENT) {      // Child wants to determine its own size.... find out how      // big it should be      resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;      resultMode = MeasureSpec.UNSPECIFIED;    }    break;  }  return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}

  在這段代碼對于MeasureSpec的獲取主要是根據父容器的MeasureSpec和View本身的LayoutParams。下面通過一張表格來看一下它們之間的對應關系。

  

       到這里通過getChildMeasureSpec方法獲取到子View的MeasureSpec以后,便調用View的Measure方法,開始對View進行測量。
  正如剛才說的那樣對于ViewGroup它是一個抽象類,并沒有重寫View的onMeasure方法。但是到具體的ViewGroup時,例如FrameLayout,LinearLayout,RelativeLayout等,它們通過重寫onMeasure方法來來完成自身以及子View的Measure過程。下面以FrameLayout為例,看一下的Measure過程。在FrameLayout中,它的Measure過程也算是比較簡單,下面就來看一下FrameLayout中的onMeasure方法。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  int count = getChildCount();  final boolean measureMatchParentChildren =      MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||      MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;  mMatchParentChildren.clear();  int maxHeight = 0;  int maxWidth = 0;  int childState = 0;  for (int i = 0; i < count; i++) {    final View child = getChildAt(i);    if (mMeasureAllChildren || child.getVisibility() != GONE) {      measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);      final LayoutParams lp = (LayoutParams) child.getLayoutParams();      maxWidth = Math.max(maxWidth,          child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);      maxHeight = Math.max(maxHeight,          child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);      childState = combineMeasuredStates(childState, child.getMeasuredState());      if (measureMatchParentChildren) {        if (lp.width == LayoutParams.MATCH_PARENT ||            lp.height == LayoutParams.MATCH_PARENT) {          mMatchParentChildren.add(child);        }      }    }  }  // Account for padding too  maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();  maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();  // Check against our minimum height and width  maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());  maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());  // Check against our foreground's minimum height and width  final Drawable drawable = getForeground();  if (drawable != null) {    maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());    maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());  }  setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),      resolveSizeAndState(maxHeight, heightMeasureSpec,          childState << MEASURED_HEIGHT_STATE_SHIFT));  count = mMatchParentChildren.size();  if (count > 1) {    for (int i = 0; i < count; i++) {      final View child = mMatchParentChildren.get(i);      final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();      final int childWidthMeasureSpec;      if (lp.width == LayoutParams.MATCH_PARENT) {        final int width = Math.max(0, getMeasuredWidth()            - getPaddingLeftWithForeground() - getPaddingRightWithForeground()            - lp.leftMargin - lp.rightMargin);        childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(            width, MeasureSpec.EXACTLY);      } else {        childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,            getPaddingLeftWithForeground() + getPaddingRightWithForeground() +            lp.leftMargin + lp.rightMargin,            lp.width);      }      final int childHeightMeasureSpec;      if (lp.height == LayoutParams.MATCH_PARENT) {        final int height = Math.max(0, getMeasuredHeight()            - getPaddingTopWithForeground() - getPaddingBottomWithForeground()            - lp.topMargin - lp.bottomMargin);        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(            height, MeasureSpec.EXACTLY);      } else {        childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,            getPaddingTopWithForeground() + getPaddingBottomWithForeground() +            lp.topMargin + lp.bottomMargin,            lp.height);      }      child.measure(childWidthMeasureSpec, childHeightMeasureSpec);    }  }}

  在這部分代碼中邏輯也很簡單,主要完成了兩件事。首先FrameLayout完成自身的測量過程,然后在遍歷子View,執行View的measure方法,完成View的Measure過程。在這里代碼比較簡單就不在進行詳細描述。

總結

  最后對View和ViewGroup的Measure過程做一下總結。對于View,它的Measure很簡單,在獲取到View的高和寬的測量值之后,便為其設置高和寬。而對于ViewGroup來說,除了完成自身的Measure過程以外,還需要遍歷子View,完成子View的測量過程。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 桂东县| 新田县| 浪卡子县| 温州市| 清苑县| 大兴区| 镇坪县| 大新县| 东海县| 都兰县| 海门市| 巴塘县| 松潘县| 绥棱县| 建平县| 武义县| 库伦旗| 延寿县| 乌拉特中旗| 革吉县| 龙游县| 兴义市| 杭锦后旗| 旬阳县| 中宁县| 淮安市| 海林市| 讷河市| 东宁县| 康保县| 镇巴县| 元谋县| 太谷县| 怀安县| SHOW| 兰州市| 高碑店市| 叙永县| 康乐县| 通化县| 宣化县|