每日一問:詳細說一下 MeasureSpec.UNSPECIFIED

南塵發表於2019-06-19

詳細說一下 MeasureSpec.UNSPECIFIED

前面的文章 我留下了一個疑惑,那就是到底為什麼 NestedScrollView 要把子 View 的測量模式強行設定為 MeasureSpec.UNSPECIFIED ,這不,在鴻洋的 "wanAndroid" 中,他再次提出了這樣的問題:

MesureSpec.UNSPECIFIED

  1. 這個模式什麼時候會遇到?
  2. 遇到後怎麼處理?
  3. 有什麼注意事項?

下面摘自使用者「陳小緣啦啦啦」的回答,我覺得回答的非常到位,特別在這裡和大家分享一下。

UNSPECIFID,就是未指定的意思,在這個模式下父控制元件不會干涉子 View 想要多大的尺寸。
那麼,這個模式什麼時候會onMeasure() 裡遇到呢?其實是取決於它的父容器。
就拿最常用的 RecyclerView 做例子,在 Item 進行 measure() 時,如果列表可滾動,並且 Item 的寬或高設定了 wrap_content 的話,那麼接下來,itemView 的 onMeasure( )方法的測量模式就會變成 MeasureSpec.UNSPECIFIED
我們不妨開啟 RecyclerView 原始碼,會在 getChildMeasureSpec() 方法裡看到這麼一句註釋:

MATCH_PARENT can't be applied since we can scroll in this dimension, wrap instead using UNSPECIFIED.

它想表達的是:在可滾動的ViewGroup中,不應該限制 Item 的尺寸(如果是水平滾動,就不限制寬度),為什麼呢? 因為是可以滾動的,就算 Item 有多寬,有多高,通過滾動也一樣能看到滾動前被遮擋的部分。

這裡其實也就回答了我之前詢問的 NestedScrollView 要強行設定 Item 為 UNSPECIFIED 的原因。
有同學可能會有疑問: 我設定 wrap_content,在 onMeasure() 中應該收到的是 AT_MOST 才對啊,為什麼要強制變成 UNSPECIFIED

這是因為考慮到 Item 的尺寸有可能超出這個可滾動的 ViewGroup 的尺寸,而在 AT_MOST 模式下,你的尺寸不能超出你所在的 ViewGroup 的尺寸,最多隻能等於,所以用 UNSPECIFIED會更合適,這個模式下你想要多大就多大。

那麼,我們在自定義 View 的時候,在測量時發現是 UNSPECIFIED 模式時,應該怎麼做呢?

這個就比較自由了,既然尺寸由自己決定,那麼我可以寫死為 50,也可以固定為 200。但還是建議結合實際需求來定義咯。

比如 ImageView,它的做法就是:有設定圖片內容(drawable)的話,會直接使用這個 drawable 的尺寸,但不會超過指定的 MaxWidthMaxHeight, 沒有內容的話就是 0。而 TextView 處理 UNSPECIFIED 的方式,和 AT_MOST 是一樣的。

當然了,這些尺寸都不一定等於最後 layout 出來的尺寸,因為最後決定子 View 位置和大小的,是在 onLayout() 方法中,在這裡你完全可以無視這些尺寸,去 layout()成自己想要的樣子。不過,一般不會這麼做。

相關文章