Android學習“易錯” 系列:老司機都掉的坑,你進去了嗎?
之前分享了很多面試題,蠻多都自帶正確答案,來幾期易錯系列,大家一起醒醒神。
今天來個簡單的開胃一下。
這個知識點,我定義為在面試過程中 答對不加分,答錯扣分的題目,不過在我以前面試經歷中,能完整說上來的同學不多。
我們一起來看看大家對這個知識的掌握程度吧。
在早期的部落格的裡面,很多時候,見到有如下的介紹:
- 如果你的
View
設定了match_parent
,則在onMeasure
中得到的測量模式為:EXACTLY
; - 如果設定了
wrap_conent
,則對應測量模式為:AT_MOST
; - 還剩下一個
UNSPECIFIED
大家不用管,不常用;
上述描述每句話都可以認為是錯的。
那麼今天我要搞清楚幾個問題:
-
match_parent / wrap_conent
一定對應EXACTLY/ AT_MOST
嗎 ? - 測量模式到底是由哪些因素確定的?
-
UNSPECIFIED
真的不常見嗎?
1.
match_parent和wrap_content
就一定對應
MeasureSpec.EXACTLY
和
MeasureSpec.AT_MOST
嗎?
肯定不是。
為什麼呢?
因為
View
在
measure
時,它的寬高
MeasureSpec
完全是取決於父容器,父容器傳的是什麼它收到的就是什麼。
如果這個父容器的
onMeasure
方法裡面寫死了每個子
View
的
MeasureSpec
的
Mode
為
UNSPECIFIED
的話,那麼無論你在
xml
佈局或者
LayoutParams
中怎麼設定寬高都好,最終子
View
的
onMeasure
收到的也是
UNSPECIFIED
。
好吧,故意手動指定的不算。
就以正常的角度來看:
我們都知道,自定義
ViewGroup
過程中,需要在
onMeasure
裡面對子
View
進行測量。
在測量子
View
時,往往會透過
measureChild
、
measureChildWithMargins
方法來完成(比如
FrameLayout
、
LinearLayout
、
CoordinatorLayout
、
ViewPager2
)。
或者呼叫
ViewGroup
的靜態方法
getChildMeasureSpec
來直接獲取目標子
View
的
MeasureSpec
,然後手動
measure
(比如
ScrollView
、
NestedScrollView
、
DrawerLayout
、
TabLayout
、
ConstraintLayout
)。
其實,
measureChild
和
measureChildWithMargins
裡面也是會透過
getChildMeasureSpec
方法來獲取
MeasureSpec
的,也就是說,上面提到的這些容器,在測量它們的子
View
之前,都是先透過
getChildMeasureSpec
方法來獲取子
View
的寬高
MeasureSpec
,然後傳給子
View
的
measure
方法的。
好,那我們現在來看看
getChildMeasureSpec
方法裡面做了什麼:
public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); ...... switch (specMode) { case MeasureSpec.EXACTLY: if (childDimension >= 0) { ...... resultMode = MeasureSpec.EXACTLY; } else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) { ...... resultMode = MeasureSpec.EXACTLY; } else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) { ...... resultMode = MeasureSpec.AT_MOST; } break; case MeasureSpec.AT_MOST: if (childDimension >= 0) { ...... resultMode = MeasureSpec.EXACTLY; } else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) { ...... resultMode = MeasureSpec.AT_MOST; } else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) { ...... resultMode = MeasureSpec.AT_MOST; } break; case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { ...... resultMode = MeasureSpec.EXACTLY; } else if (childDimension == ViewGroup.LayoutParams.MATCH_PARENT) { ...... resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == ViewGroup.LayoutParams.WRAP_CONTENT) { ...... resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
可以看到:
在
父容器的
specMode為EXACTLY
時,一切正常(子
View
尺寸指定為
match_parent
或精確的
dimen
值時,
Mode = EXACTLY
,尺寸指定為
wrap_content
則
Mode = AT_MOST
);
當
父容器
specMode為AT_MOST
的時候,呵呵,可以看到,除了指定了
dimen
值之外,無論設定為
match_parent
或
wrap_content
,
Mode
最終都是會變成
AT_MOST
;
如果
父容器
specMode
是
UNSPECIFIED
的話,跟上面的邏輯差不多,都是會變成
UNSPECIFIED
的,除非指定了精確的
dimen
值;
所以,
View
的
onMeasure
方法中收到的寬高
MeasureSpec
,不完全是由
xml
佈局中設定的寬高或
LayoutParams
的寬高值決定的。
2. 有哪些因素影響著MeasureSpec的mode?
從剛剛的getChildMeasureSpec方法中可以看出,影響著View測量模式的因素主要是該View所屬容器的測量模式。
也就是說,正常情況下(不是故意亂設定),View的測量模式是由:
**它自身的
LayoutParams
設定的值 **+
父容器的測量模式來決定的。
為什麼大家都說
MeasureSpec.UNSPECIFIED
不常見呢?
大家都覺得這個模式不常見,很可能就是因為在編寫佈局時,
View
的寬高只能選擇
match_parent
、
wrap_content
或者直接指定一個精確的尺寸,相對來說,
MeasureSpec.UNSPECIFIED
就顯得不太透明瞭,因為在日常開發中,如不需定製
View
的話,基本上不會直接接觸到。
3. MeasureSpec.UNSPECIFIED是不是真的不常見?
在日常定製
View
時,確實很少會專門針對這個模式去做特殊處理,大多數情況下,都會把它當成
MeasureSpec.AT_MOST
一樣看待,就比如最最常用的
TextView
,它在測量時也是不會區分
UNSPECIFIED和AT_MOST
的。
不過,雖說這個模式比較少直接接觸到,
但很多場景下,我們已經在不知不覺中用上了,比如
RecyclerView
的
Item
,如果
Item
的寬/高是
wrap_content
且列表可滾動的話,那麼
Item
的寬/高的測量模式就會是
UNSPECIFIED
。
還有就是
NestedScrollView
和
ScrollView
,因為它們都是擴充套件自
FrameLayout
,所以它們的子
View
會測量兩次,第一次測量時,子
View
的
heightMeasureSpec
的模式是寫死為
UNSPECIFIED
的。
我們在自定義
ViewGroup
過程中,如果允許子
View
的尺寸比
ViewGroup
大的話,在測量子
View
時就可以把
Mode
指定為
UNSPECIFIED
。
好了,希望這次你徹底弄明白了自定義控制元件的測量模式相關知識。
另外也有人給我發了個圖,說這個圖就能說明白了,其實這個圖也有一點點小問題:
我畫圈的地方,這個值
不一定是 0, 不過大多情況下
UNSPECIFIED
這個模式一般不在乎這個
size
。
最後
今天就講到這裡,我的Android核心技術學習大綱,獲取相關內容來我的GitHub一起玩耍:
對於進階這條路而言,學習是會有回報的!
你把你的時間投資在學習上,就意味著你可以收穫技能,更有機會增加收入。
分享我的
Android學習PDF大全
這份Android學習PDF大全真的包含了方方面面了,內含Java基礎知識點、Android基礎、Android進階延伸、演算法合集等等
我的這份學習合集,可以有效的幫助大家掌握知識點。
總之也是在這裡幫助大家學習提升進階,也節省大家在網上搜尋資料的時間來學習,也可以分享給身邊好友一起學習
Android學習PDF大全關注我看個人介紹,或者私信我獲取
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952849/viewspace-2683658/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 避免踩坑:易盾安全老司機起底Android九大漏洞,附解決建議Android
- 安卓易學,爬坑不易—騰訊老司機的RecyclerView區域性重新整理爬坑之路安卓View
- 老司機帶你快速上手除錯Flutter專案除錯Flutter
- Python老司機告訴你,學習Python應該讀哪些書!Python
- 你掉進過“偽敏捷”的陷阱嗎?敏捷
- 老司機常用的幾個JavaScript除錯技巧JavaScript除錯
- 老司機的思考
- Java-Integer好大一坑,一不小心就掉進去了Java
- 老司機帶你玩轉Radare2
- 老司機帶你深入 Laravel 之 ServiceProvider 原理LaravelIDE
- 老司機帶你實現 Laravel 之管道Laravel
- 老司機避坑指南:如何快速搞定微服務架構?微服務架構
- 學習前端遇到瓶頸了?這些‘好’習慣都會毀掉你前端
- 關於學習Python的疑問,你都清楚了嗎?Python
- 關於Python學習的方法以及技巧,你都知道嗎?Python
- 老司機帶你深入理解 Laravel 之 FacadeLaravel
- Android加固和簽名的那些坑(防掉坑技巧)Android
- 學習Python這些面試題你都知道嗎?Python面試題
- 老司機的神兵利器-效率工具
- 老司機帶你用 PHP 實現 Websocket 協議PHPWeb協議
- 老司機帶你用python來爬取妹子圖Python
- 2019年,你熬過去了嗎?
- 小司機帶你學習單例模式的六種姿勢!單例模式
- 老司機都怕的“左轉彎”, 谷歌無人車憑什麼能做到谷歌
- 數字化轉型的七大誤區,你陷進去了嗎?
- 老司機帶你用go實現路由的鏈式定義Go路由
- 老司機告訴你,我們究竟想要怎樣的遊戲?遊戲
- 老司機帶你深入分析 Laravel 響應之一Laravel
- 老司機帶你領悟 Laravel 之授權系統Laravel
- 老司機 iOS 週報 #10iOS
- 老司機 iOS 週報 #9iOS
- 老司機 iOS 週報 #5iOS
- 老司機 iOS 週報 #11iOS
- 多年java開發經驗老司機告訴你,0基礎如何學Java?Java
- 「AI+教育」新進階,你想跟 AI 智慧老師學習嗎?AI
- 老司機 iOS 週報 #92 | 2019.11.25iOS
- 老司機 iOS 週報 #93 | 2019.12.02iOS
- 老司機 iOS 週報 #78 | 2019.08.05iOS