詳細說一下 MeasureSpec.UNSPECIFIED
前面的文章 我留下了一個疑惑,那就是到底為什麼 NestedScrollView
要把子 View 的測量模式強行設定為 MeasureSpec.UNSPECIFIED
,這不,在鴻洋的 "wanAndroid" 中,他再次提出了這樣的問題:
MesureSpec.UNSPECIFIED
- 這個模式什麼時候會遇到?
- 遇到後怎麼處理?
- 有什麼注意事項?
下面摘自使用者「陳小緣啦啦啦」的回答,我覺得回答的非常到位,特別在這裡和大家分享一下。
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 的尺寸,但不會超過指定的 MaxWidth
或 MaxHeight
, 沒有內容的話就是 0。而 TextView
處理 UNSPECIFIED
的方式,和 AT_MOST
是一樣的。
當然了,這些尺寸都不一定等於最後 layout
出來的尺寸,因為最後決定子 View
位置和大小的,是在 onLayout()
方法中,在這裡你完全可以無視這些尺寸,去 layout()
成自己想要的樣子。不過,一般不會這麼做。