獲取窗口可視區(qū)域大小: getWindowVisibleDisplayFrame() 是View類下的一個方法厚棵,從方法的名字就可以看出吧寺,它是用來獲取當(dāng)前窗口可視區(qū)域大小的涛酗。
此方法的原型為
/**
* Retrieve the overall visible display size in which the window this view is
* attached to has been positioned in. This takes into account screen
* decorations above the window, for both cases where the window itself
* is being position inside of them or the window is being placed under
* then and covered insets are used for the window to position its content
* inside. In effect, this tells you the available area where content can
* be placed and remain visible to users.
*
* <p>This function requires an IPC back to the window manager to retrieve
* the requested information, so should not be used in performance critical
* code like drawing.
*
* @param outRect Filled in with the visible display frame. If the view
* is not attached to a window, this is simply the raw display size.
*/
public void getWindowVisibleDisplayFrame(Rect outRect);
它接受一個Rect對象作為參數(shù),執(zhí)行過程中會根據(jù)當(dāng)前窗口可視區(qū)域大小更新outRect的值,執(zhí)行完畢后量九,就可以根據(jù)更新后的outRect來確定窗口可視區(qū)域的大小。所以正如outRect的名字所見颂碧,它是一個輸出參數(shù)荠列,后面如果提到getWindowVisibleDisplayFrame()方法的返回結(jié)果,指的也是參數(shù)outRect更新后的結(jié)果载城,getWindowVisibleDisplayFrame()本身是沒有返回值的肌似。此外,由于是輸出參數(shù)诉瓦,outRect必須不為null川队,一般在使用前會先new一個沒有大小的Rect對象力细,將其作為參數(shù)傳給getWindowVisibleDisplayFrame()方法
Rect rect = new Rect();
view.getWindowVisibleDisplayFrame(rect);
由于getWindowVisibleDisplayFrame()方法是View類下的一個方法,所以只能通過View對象來調(diào)用固额。一個窗口中通常都會有多個View眠蚂,getWindowVisibleDisplayFrame()方法的返回結(jié)果和該窗口中選取的View并沒有關(guān)系。在某個時刻斗躏,使用當(dāng)前窗口中的任意View執(zhí)行g(shù)etWindowVisibleDisplayFrame()返回的結(jié)果都是一樣的逝慧。這也很容易理解,getWindowVisibleDisplayFrame()方法返回的是窗口的可視區(qū)域大小啄糙,并非某個View的可視區(qū)域大小笛臣,所以用窗口中的任意View來執(zhí)行都是沒有差別的。一般來說可以使用當(dāng)前窗口的根View來執(zhí)行這個方法隧饼,也就是調(diào)用Window對象的getDecorView().getWindowVisibleDisplayFrame()來獲取沈堡。在Acitivity和Dialog中可以用getWindow()來得到Window對象,合起來就是這樣的燕雁。
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
這里需要注意的是踱蛀,由于getWindowVisibleDisplayFrame()方法是用來獲取某個窗口的可視區(qū)域大小,所以調(diào)用getWindowVisibleDisplayFrame()方法的View必須包含在該窗口中贵白,如果是一個孤立的View率拒,或者包含在其他窗口中,是沒有意義的禁荒。例如
Rect rect = new Rect();
// 這個new出來的View并沒有add到任何窗口上猬膨,所以調(diào)用它的getWindowVisibleDisplayFrame()方法是沒有意義的。
new View(this).getWindowVisibleDisplayFrame(rect);
雖然getWindowVisibleDisplayFrame()的執(zhí)行結(jié)果和窗口中View的選取沒有關(guān)系呛伴,但是卻和執(zhí)行此方法時View的狀態(tài)有關(guān)勃痴。由于此方法是用來獲取窗口的可視區(qū)域大小,所以如果調(diào)用此方法時热康,調(diào)用的View對象還沒有附著(attach)到任何Window上沛申,那么執(zhí)行此方法將不會得到實際的某個窗口的可視區(qū)域大小,只有View對象已經(jīng)attach到Window上之后姐军,調(diào)用此方法才能得到真實的窗口的可視區(qū)域大小铁材。當(dāng)調(diào)用的View對象還沒有attach到Window時,getWindowVisibleDisplayFrame()方法會估計出一個可能的可視區(qū)域大小奕锌,這個大小通常是設(shè)備的屏幕尺寸(以像素為單位)著觉,由于它并不代表真實的窗口可視區(qū)域大小,所以這個數(shù)值的意義不大惊暴。
由于在Activity/Fragment/Dialog的onCreate()方法中饼丘,View對象還沒有attach到Window上,所以在onCreate()方法中執(zhí)行某個View的getWindowVisibleDisplayFrame()方法辽话,是不會得到當(dāng)前Window實際的可視區(qū)域大小的肄鸽。
在Activity的onAttachedToWindow()方法中執(zhí)行g(shù)etWindowVisibleDisplayFrame()卫病,也是得不到當(dāng)前Window實際的可視區(qū)域大小的。因為Activity的onAttachedToWindow()方法執(zhí)行時典徘,表示當(dāng)前Window被attach到window manager中忽肛,Window中的View仍然沒有attach到Window上。
View attach到Window之后烂斋,View對象的onAttachedToWindow()方法會被執(zhí)行屹逛,理論上來說,在自定義View的onAttachedToWindow()方法中執(zhí)行g(shù)etWindowVisibleDisplayFrame()應(yīng)該可以得到當(dāng)前Window實際的可視區(qū)域大小汛骂,但實際情況卻并非如此罕模。在自定義View的onAttachedToWindow()方法中執(zhí)行g(shù)etWindowVisibleDisplayFrame()會概率性的出現(xiàn)不同的結(jié)果,有時返回的rect對象大小是0帘瞭,有時則是設(shè)備的屏幕尺寸淑掌,但都不是當(dāng)前Window實際的可視區(qū)域大小。具體原因未知蝶念,沒有仔細(xì)研究抛腕。所以,也不要在View對象的onAttachedToWindow()方法中執(zhí)行g(shù)etWindowVisibleDisplayFrame()媒殉。
要想得到當(dāng)前Window實際的可視區(qū)域大小担敌,可以在Activity/Fragment/Dialog的onWindowFocusChanged()方法中執(zhí)行g(shù)etWindowVisibleDisplayFrame()。這時Window中的View對象都已經(jīng)attach到Window上廷蓉。
還有一點需要說明的是全封,getWindowVisibleDisplayFrame()的執(zhí)行結(jié)果和View是否可見沒有關(guān)系。View無論是VISIBLE桃犬,還是INVISIBLE或者GONE刹悴,對getWindowVisibleDisplayFrame()的執(zhí)行結(jié)果沒有影響。當(dāng)View是INVISIBLE的時候攒暇,其onDraw()方法不會被調(diào)用土匀,當(dāng)View是GONE的時候,其onDraw()和onLayout()方法不會被調(diào)用形用。但無論是INVISIBLE或者GONE就轧,onAttachedToWindow()都會被調(diào)用,也就是說View會被attach到Window上尾序,所以即使View是INVISIBLE或者GONE的钓丰,getWindowVisibleDisplayFrame()也能夠正確的返回。
getWindowVisibleDisplayFrame()的執(zhí)行結(jié)果分析
這里所說的getWindowVisibleDisplayFrame()執(zhí)行結(jié)果均是指當(dāng)前Window實際的可視區(qū)域大小每币,對調(diào)用的View對象還沒有attach到Window時,getWindowVisibleDisplayFrame()方法估計出可視區(qū)域大小的情況不做討論琢歇。
getWindowVisibleDisplayFrame()執(zhí)行結(jié)果和以下因素有關(guān)
- 系統(tǒng)狀態(tài)欄
系統(tǒng)狀態(tài)欄會影響getWindowVisibleDisplayFrame()執(zhí)行結(jié)果outRect中的top屬性的值兰怠。
如果窗口是全屏的梦鉴,也就是設(shè)置了flags為WindowManager.LayoutParams.FLAG_FULLSCREEN,或者Android:windowFullscreen設(shè)置為true揭保,則outRect中的top屬性不受狀態(tài)欄影響肥橙,其值始終為0。否則秸侣,outRect中的top屬性值將會受到系統(tǒng)狀態(tài)欄的影響存筏。
如果窗口的LayoutParams的height設(shè)置為WindowManager.LayoutParams.MATCH_PARENT,則outRect中的top值會等于系統(tǒng)狀態(tài)欄的高度味榛,如果窗口的LayoutParams的height設(shè)置為WindowManager.LayoutParams.WRAP_CONTENT或者某個具體的值椭坚,則outRect中的top值會等于系統(tǒng)狀態(tài)欄和窗口重疊區(qū)域的高度,如果沒有重疊搏色,則是0善茎。
例如,屏幕高度為1920频轿,窗口高度設(shè)置為1900垂涯,窗口居中顯示。這時窗口上下距離屏幕各有10個像素的距離航邢。假如系統(tǒng)狀態(tài)欄高度為60耕赘,窗口和狀態(tài)欄的重疊區(qū)域的高度就是50。因此膳殷,getWindowVisibleDisplayFrame()返回的outRect中的top值為50鞠苟。
上面這幾點可以歸結(jié)為一點,outRect中的top值等于系統(tǒng)狀態(tài)欄在理論上會對窗口上方所在位置產(chǎn)生的影響秽之。
如果窗口是全屏的当娱,系統(tǒng)狀態(tài)欄將無法影響窗口上方位置,因此考榨,outRect中的top值始終為0跨细。如果窗口的LayoutParams的height設(shè)置為WindowManager.LayoutParams.MATCH_PARENT,則理論上窗口將到達(dá)屏幕最上方的位置河质,但是由于狀態(tài)欄的存在冀惭,會壓迫窗口位置到狀態(tài)欄下方,因此掀鹅,outRect中的top值等于系統(tǒng)狀態(tài)欄的高度散休。如果窗口的LayoutParams的height設(shè)置為WindowManager.LayoutParams.WRAP_CONTENT或者某個具體的值,且窗口和狀態(tài)欄存在重疊乐尊,則這時狀態(tài)欄同樣會試圖壓迫窗口位置到狀態(tài)欄下方戚丸,其位移就是重疊區(qū)域的高度,因此outRect中的top值等于重疊區(qū)域的高度扔嵌。需要注意的是限府,這里狀態(tài)欄對窗口位置的影響并不會實際生效夺颤,也就是窗口仍然會和狀態(tài)欄重疊,因此狀態(tài)欄對窗口位置的影響是一種理論上的胁勺,并非一定會生效世澜。
- 虛擬鍵盤
虛擬鍵盤會影響getWindowVisibleDisplayFrame()執(zhí)行結(jié)果outRect中的bottom屬性的值。
如果虛擬鍵盤是隱藏的署穗,則outRect中的bottom屬性的值將始終等于屏幕高度(實際上還要減去虛擬按鍵欄的高度寥裂,這里先忽略虛擬按鍵)。如果虛擬鍵盤是顯示的案疲,outRect中的bottom屬性的值將等于屏幕高度減去理論上虛擬鍵盤會對窗口位置產(chǎn)生的影響封恰。如果窗口高度是MATCH_PARENT的,則outRect中的bottom屬性的值將等于屏幕高度減去虛擬鍵盤的高度络拌。
同樣的例子俭驮,屏幕高度為1920,窗口高度設(shè)置為1900春贸,窗口居中顯示混萝。這時窗口上下距離屏幕各有10個像素的距離。假如虛擬鍵盤高度為600萍恕,窗口和虛擬鍵盤的重疊區(qū)域的高度就是590逸嘀。因此,getWindowVisibleDisplayFrame()返回的outRect中的bottom值為1920 - 590允粤。
- 虛擬按鍵欄
虛擬按鍵欄會影響getWindowVisibleDisplayFrame()執(zhí)行結(jié)果outRect中的bottom屬性的值崭倘。
這里只考慮軟鍵盤是隱藏的情況,如果軟鍵盤是顯示的类垫,則軟鍵盤和虛擬按鍵欄對outRect中的bottom屬性的值的影響將會疊加司光。
如果虛擬按鍵欄是隱藏的,則outRect中的bottom屬性的值將始終等于屏幕高度悉患。如果虛擬按鍵是顯示的残家,outRect中的bottom屬性的值將等于屏幕高度減去理論上虛擬按鍵會對窗口位置產(chǎn)生的影響。如果窗口高度是MATCH_PARENT的售躁,則outRect中的bottom屬性的值將等于屏幕高度減去虛擬按鍵的高度坞淮。
這里不再舉例說明。
綜上所述陪捷,getWindowVisibleDisplayFrame()執(zhí)行結(jié)果會受到系統(tǒng)狀態(tài)欄回窘,系統(tǒng)軟鍵盤,系統(tǒng)虛擬按鍵的影響市袖。
這里需要注意的是啡直,getWindowVisibleDisplayFrame()的結(jié)果并不是該窗口實際的大小(雖然它和窗口的大小有一定關(guān)系)。例如一個居中顯示的對話框付枫,它的實際高度只有500px烹玉,它和系統(tǒng)狀態(tài)欄驰怎,系統(tǒng)軟鍵盤阐滩,系統(tǒng)虛擬按鍵欄都沒有重疊,那么getWindowVisibleDisplayFrame()的結(jié)果就是設(shè)備的尺寸大小县忌,而不是該對話框的實際大小掂榔。
此外,雖然方法名字中有一個Visible症杏,但是getWindowVisibleDisplayFrame()的結(jié)果并不受該窗口是否在被其他窗口遮擋的影響装获。即使該窗口已經(jīng)被切換到后臺,只要該窗口還沒有dettach厉颤,getWindowVisibleDisplayFrame()的結(jié)果就不會變化穴豫。
getWindowVisibleDisplayFrame()的應(yīng)用
在android系統(tǒng)中,并沒有提供api來獲取系統(tǒng)狀態(tài)欄逼友,系統(tǒng)軟鍵盤和系統(tǒng)虛擬按鍵欄的高度精肃,但在應(yīng)用中有時會需要獲取這幾個數(shù)值。由于getWindowVisibleDisplayFrame()返回結(jié)果會受到系統(tǒng)狀態(tài)欄帜乞,系統(tǒng)軟鍵盤司抱,系統(tǒng)虛擬按鍵的影響。因此黎烈,這個api常常被用來獲取系統(tǒng)狀態(tài)欄习柠,系統(tǒng)軟鍵盤和系統(tǒng)虛擬按鍵欄的高度。
對系統(tǒng)狀態(tài)欄高度照棋,獲取一個非全屏资溃,且窗口的LayoutParams的height設(shè)置為WindowManager.LayoutParams.MATCH_PARENT的窗口可視區(qū)域大小,其top值就是狀態(tài)欄的高度烈炭。
對系統(tǒng)軟鍵盤溶锭,獲取一個高度是MATCH_PARENT的窗口在軟鍵盤顯示和隱藏兩種不同狀態(tài)下的可視區(qū)域大小,將bottom值相減就可以得到軟鍵盤的高度梳庆。
對系統(tǒng)系統(tǒng)虛擬按鍵欄暖途,獲取一個高度是MATCH_PARENT的窗口在虛擬按鍵顯示和隱藏兩種不同狀態(tài)下的可視區(qū)域大小,將bottom值相減就可以得到虛擬按鍵的高度膏执。
getWindowVisibleDisplayFrame()的性能問題
在getWindowVisibleDisplayFrame()方法的注釋中有這樣一段
說明getWindowVisibleDisplayFrame()方法是通過IPC方式從window manager中獲取到這個信息的驻售,相對來說它的開銷會比較大,因此不適合放在對性能要求很高的地方調(diào)用更米,例如View繪制的代碼中欺栗。