Android開發學習之View測量的內置常用方法

 

measureChildWithMargins

代碼如下


 
  1. protected void measureChildWithMargins(View child,

  2. int parentWidthMeasureSpec, int widthUsed,

  3. int parentHeightMeasureSpec, int heightUsed) {

  4. final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

  5.  
  6. final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,

  7. mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin

  8. + widthUsed, lp.width);

  9. final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,

  10. mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin

  11. + heightUsed, lp.height);

  12.  
  13. child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

  14. }

主要調用了getChildMeasureSpec()方法

 

getChildMeasureSpec

代碼如下


 
  1. public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

  2. // 以寬度爲例,傳入的參數分別是:父view的widthMeasureSpec、已用寬度(子view左右內外間距+已用的間距)、子view要求的寬度

  3.  
  4. int specMode = MeasureSpec.getMode(spec);

  5. int specSize = MeasureSpec.getSize(spec);

  6.  
  7. int size = Math.max(0, specSize - padding); // 父寬度 - 子view的內外間距

  8. // 也就是子view的最大寬度

  9.  
  10. int resultSize = 0;

  11. int resultMode = 0;

  12.  
  13. switch (specMode) {

  14. // Parent has imposed an exact size on us

  15. case MeasureSpec.EXACTLY:

  16. if (childDimension >= 0) {

  17. resultSize = childDimension;

  18. resultMode = MeasureSpec.EXACTLY;

  19. } else if (childDimension == LayoutParams.MATCH_PARENT) {

  20. // Child wants to be our size. So be it.

  21. resultSize = size;

  22. resultMode = MeasureSpec.EXACTLY;

  23. } else if (childDimension == LayoutParams.WRAP_CONTENT) {

  24. // Child wants to determine its own size. It can't be

  25. // bigger than us.

  26. resultSize = size;

  27. resultMode = MeasureSpec.AT_MOST;

  28. }

  29. break;

  30.  
  31. // Parent has imposed a maximum size on us

  32. case MeasureSpec.AT_MOST:

  33. if (childDimension >= 0) {

  34. // Child wants a specific size... so be it

  35. resultSize = childDimension;

  36. resultMode = MeasureSpec.EXACTLY;

  37. } else if (childDimension == LayoutParams.MATCH_PARENT) {

  38. // Child wants to be our size, but our size is not fixed.

  39. // Constrain child to not be bigger than us.

  40. resultSize = size;

  41. resultMode = MeasureSpec.AT_MOST;

  42. } else if (childDimension == LayoutParams.WRAP_CONTENT) {

  43. // Child wants to determine its own size. It can't be

  44. // bigger than us.

  45. resultSize = size;

  46. resultMode = MeasureSpec.AT_MOST;

  47. }

  48. break;

  49.  
  50. // Parent asked to see how big we want to be

  51. case MeasureSpec.UNSPECIFIED:

  52. if (childDimension >= 0) {

  53. // Child wants a specific size... let him have it

  54. resultSize = childDimension;

  55. resultMode = MeasureSpec.EXACTLY;

  56. } else if (childDimension == LayoutParams.MATCH_PARENT) {

  57. // Child wants to be our size... find out how big it should

  58. // be. sdk < 23時,sUseZeroUnspecifiedMeasureSpec爲true

  59. resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;

  60. resultMode = MeasureSpec.UNSPECIFIED;

  61. } else if (childDimension == LayoutParams.WRAP_CONTENT) {

  62. // Child wants to determine its own size.... find out how

  63. // big it should be.

  64. resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;

  65. resultMode = MeasureSpec.UNSPECIFIED;

  66. }

  67. break;

  68. }

  69. //noinspection ResourceType

  70. return MeasureSpec.makeMeasureSpec(resultSize, resultMode);

  71. }

 

combineMeasuredState


 
  1. public static int combineMeasuredStates(int curState, int newState) {

  2. return curState | newState;

  3. }

沒啥好說的,合併兩個state

 

getMeasuredState


 
  1. public final int getMeasuredState() {

  2. return (mMeasuredWidth&MEASURED_STATE_MASK)

  3. | ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT)

  4. & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));

  5. }

通過位運算合併高度和寬度的state,結果就是第1個字節是寬度的state,第3個字節是高度的state

 

resolveSizeAndState


 
  1. public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { // size是自己需要的尺寸,measureSpec是父view指定的尺寸

  2. final int specMode = MeasureSpec.getMode(measureSpec);

  3. final int specSize = MeasureSpec.getSize(measureSpec); // 父view指定的尺寸

  4. final int result;

  5. switch (specMode) {

  6. case MeasureSpec.AT_MOST:

  7. if (specSize < size) {

  8. result = specSize | MEASURED_STATE_TOO_SMALL; // 設定標誌位,表示父view得分配的再大一些

  9. } else {

  10. result = size;

  11. }

  12. break;

  13. case MeasureSpec.EXACTLY:

  14. result = specSize;

  15. break;

  16. case MeasureSpec.UNSPECIFIED:

  17. default:

  18. result = size;

  19. }

  20. return result | (childMeasuredState & MEASURED_STATE_MASK);

  21. }

最後保存狀態時,由於childMeasureState是第一個字節是寬度的狀態,第三個字節是高度的狀態,所以再測量寬度高度時調用此方法,要對childState進行位運算

 

setMeasuredDimension

這個是用來保存當前view尺寸的,當我們使用自定義view並且覆寫了onMeasure()方法時,不在最後調用這個方法,直接就報錯了


 
  1. protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {

  2. boolean optical = isLayoutModeOptical(this);

  3. if (optical != isLayoutModeOptical(mParent)) { // layout_mode是LAYOUT_MODE_OPTICAL的情況很少出現,所以測量過程中涉及optical的,我們一般都可以直接忽略不計

  4. Insets insets = getOpticalInsets();

  5. int opticalWidth = insets.left + insets.right;

  6. int opticalHeight = insets.top + insets.bottom;

  7.  
  8. measuredWidth += optical ? opticalWidth : -opticalWidth;

  9. measuredHeight += optical ? opticalHeight : -opticalHeight;

  10. }

  11. setMeasuredDimensionRaw(measuredWidth, measuredHeight);

  12. }

直接調用了setMeasuredDimensionRaw()方法


 
  1. private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {

  2. mMeasuredWidth = measuredWidth;

  3. mMeasuredHeight = measuredHeight;

  4.  
  5. mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;

  6. }

嗯,就是這一步,保存了measuredHeight和measuredWidth.不過,這裏要說明一下,measuredHeight/Width高八位是狀態,低二十四位纔是真正的尺寸,這也就是View.getMeasuredWidth/Height和View.getMeasuredWidth/HeightAndState()方法的區別所在

 

getDefaultSize


 
  1. public static int getDefaultSize(int size, int measureSpec) {

  2. int result = size; // size是view的默認尺寸

  3. int specMode = MeasureSpec.getMode(measureSpec);

  4. int specSize = MeasureSpec.getSize(measureSpec);

  5.  
  6. switch (specMode) {

  7. case MeasureSpec.UNSPECIFIED:

  8. result = size;

  9. break;

  10. case MeasureSpec.AT_MOST:

  11. case MeasureSpec.EXACTLY:

  12. result = specSize;

  13. break;

  14. }

  15. return result;

  16. }

 

getSuggestMinimunHeight/Width

以高度爲例,代碼如下


 
  1. protected int getSuggestedMinimumHeight() {

  2. return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());

  3. }

涉及view的背景,mBackground是Drawable類的,它的getMinimumHeight()由不同的子類分別實現,反正就是返回最小的高度,然後再跟view本身的最小高度取最大值(view本身的最小高度不手動設置的話,就是0)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章