1. 程式人生 > >總結來說 mode 取值 受父容器與子控件共同決定

總結來說 mode 取值 受父容器與子控件共同決定

很多 tools too data 指定 wid 完全 child 設置


在自定義控件時為了滿足特定需求,widget大都是我們自己測量的。
大家都知道測量時最重要的步驟就是重寫onMeasure方法,來計算出寬高。

這裏面的MeasureSpec 很重要,大家也都知道,它是一個java中的靜態類,它有重要的三個靜態常量和三個最重要的靜態方法。

我這裏說下MeasureSpec 的3中模式
UNSPECIFIED(未指定),父元素部隊自元素施加任何束縛,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素決定自元素的確切大小,子元素將被限定在給定的邊界裏而忽略它本身大小;
AT_MOST(至多),子元素至多達到指定大小的值。

看過很多博客,大家總結說,模式和XML布局有如下對應關系:
wrap_content-> MeasureSpec.AT_MOST
match_parent -> MeasureSpec.EXACTLY
50dp(確切值) -> MeasureSpec.EXACTLY

但是在具體開發中 我發現並不是一定這樣,然後就看了下源碼,才發現子控件的mode不只是與自身的設置有關系,它還受到父容器影響。

getChildMeasureSpec() 方法

[java] view plain copy
  1. public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
  2. int specMode = MeasureSpec.getMode(spec);
  3. int specSize = MeasureSpec.getSize(spec);
  4. int size = Math.max(0, specSize - padding);
  5. int resultSize = 0;
  6. int resultMode = 0;
  7. switch (specMode) {
  8. // Parent has imposed an exact size on us
  9. // 父容器是 EXACTLY 模式
  10. case MeasureSpec.EXACTLY:
  11. if (childDimension >= 0) {
  12. // 如果 child 希望有自己的尺寸,那麽允許它,並把它的測量模式設置為 EXACTLY
  13. resultSize = childDimension;
  14. resultMode = MeasureSpec.EXACTLY;
  15. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  16. // Child wants to be our size. So be it.
  17. // child 希望尺寸與 父容器 一樣大,那麽允許它
  18. resultSize = size;
  19. resultMode = MeasureSpec.EXACTLY;
  20. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  21. // Child wants to determine its own size. It can‘t be
  22. // bigger than us.
  23. // child 希望由自己決定自己的尺寸,但是有個前提是它不能大於 父容器 本身給的建議值。
  24. // 並且需要設置它的測量模式為 AT_MOST
  25. resultSize = size;
  26. resultMode = MeasureSpec.AT_MOST;
  27. }
  28. break;
  29. // Parent has imposed a maximum size on us
  30. // 父容器是 AT_MOST 模式,對於 父容器 本身而言,它也有個最大的尺寸約束
  31. case MeasureSpec.AT_MOST:
  32. if (childDimension >= 0) {
  33. // Child wants a specific size... so be it
  34. // 如果 child 希望有自己的尺寸,那麽允許它,並把它的測量模式設置為 EXACTLY
  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. // child 希望尺寸和 父容器 一樣大,那麽允許它,但是 父容器 本身有限制,所以也
  41. // 需要給 child 加一個限制,這個限制就是將 child 模式設置為 AT_MOST
  42. resultSize = size;
  43. resultMode = MeasureSpec.AT_MOST;
  44. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  45. // Child wants to determine its own size. It can‘t be
  46. // bigger than us.
  47. // child 希望由自己決定自己的尺寸,但是有個前提是它不能大於 父容器 本身給的建議值。
  48. // 並且需要設置它的測量模式為 AT_MOST
  49. resultSize = size;
  50. resultMode = MeasureSpec.AT_MOST;
  51. }
  52. break;
  53. // Parent asked to see how big we want to be
  54. // 父容器是 UNSPECIFIED 模式,對於 父容器 本身而言,它的期望值是想要多大就多大,讓 父容器 的 parent不要限制它。
  55. case MeasureSpec.UNSPECIFIED:
  56. if (childDimension >= 0) {
  57. // Child wants a specific size... let him have it
  58. //如果 child 希望有自己的尺寸,那麽允許它,並把它的測量模式設置為 EXACTLY
  59. resultSize = childDimension;
  60. resultMode = MeasureSpec.EXACTLY;
  61. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  62. // Child wants to be our size... find out how big it should
  63. // be
  64. // child 希望尺寸和 父容器 一樣大,那麽允許它,但是 父容器 本身也需要計算,所以只能設置為0,並且
  65. // 將child的測量模式設置為 UNSPECIFIED
  66. resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
  67. resultMode = MeasureSpec.UNSPECIFIED;
  68. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  69. // Child wants to determine its own size.... find out how
  70. // big it should be
  71. // child 希望尺寸由自己決定,一般這個時候,父容器 會給它一個 Size 作為最大值限制,
  72. // 但是 父容器 本身也需要計算,所以只能設置為0,並且沒有給child最大值的設定
  73. // 將child的測量模式設置為 UNSPECIFIED
  74. resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
  75. resultMode = MeasureSpec.UNSPECIFIED;
  76. }
  77. break;
  78. }
  79. //noinspection ResourceType
  80. return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
  81. }


總結來說 mode 取值 受父容器與子控件共同決定

父容器是MeasureSpec.EXACTLY模式
子控件是具體值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.EXACTLY
子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST

父容器是MeasureSpec.AT_MOST模式
子控件是具體值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.AT_MOST
子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST

父容器是MeasureSpec.UNSPECIFIED模式
子控件是具體值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.UNSPECIFIED
子控件是WRAP_CONTENT, mode = MeasureSpec.UNSPECIFIED

就這樣

總結來說 mode 取值 受父容器與子控件共同決定