Android解析WindowManager(二)Window的屬性

劉望舒發表於2019-02-20

相關文章
Android系統啟動系列
Android深入四大元件系列
Android解析WindowManager系列

前言

在上一篇文章我們學習了WindowManager體系,瞭解了Window和WindowManager之間的關係,這一篇我們接著來學習Window的屬性。

1.概述

上一篇文章中我們講過了Window、WindowManager和WMS之間的關係,WMS是Window的最終管理者,Window好比是員工,WMS是老闆,為了方便老闆管理員工則需要定義一些“協議”,這些“協議”就是Window的屬性,被定義在WindowManager的內部類LayoutParams中,瞭解Window的屬效能夠更好的理解WMS的內部原理。
Window的屬性有很多種,與應用開發最密切的有三種,它們分別是Type(Window的型別)、Flag(Window的標誌)和SoftInputMode(軟鍵盤相關模式),下面分別介紹這三種Window的屬性。

2.Window的型別和顯示次序

Window的型別有很多種,比如應用程式視窗、系統錯誤視窗、輸入法視窗、PopupWindow、Toast、Dialog等等。總來來說分為三大類分別是:Application Window(應用程式視窗)、Sub Windwow(子視窗)、System Window(系統視窗),每個大類又包含了很多種型別,它們都定義在WindowManager的靜態內部類LayoutParams中,接下來我們分別對這三大類進行講解。

應用程式視窗

Activity就是一個典型的應用程式視窗,應用程式視窗包含的型別如下所示。
frameworks/base/core/java/android/view/WindowManager.java

public static final int FIRST_APPLICATION_WINDOW = 1;//1
public static final int TYPE_BASE_APPLICATION   = 1;//視窗的基礎值,其他的視窗值要大於這個值
public static final int TYPE_APPLICATION        = 2;//普通的應用程式視窗型別
public static final int TYPE_APPLICATION_STARTING = 3;//應用程式啟動視窗型別,用於系統在應用程式視窗啟動前顯示的視窗。
public static final int TYPE_DRAWN_APPLICATION = 4;
public static final int LAST_APPLICATION_WINDOW = 99;//2複製程式碼

應用程式視窗共包含了以上幾種Type值,其中註釋1處的Type表示應用程式視窗型別初始值,註釋2處的Type表示應用程式視窗型別結束值,也就是說應用程式視窗的Type值範圍為1到99,這個數值的大小涉及到視窗的層級,後面會講到。

子視窗

子視窗,顧名思義,它不能獨立的存在,需要附著在其他視窗才可以,PopupWindow就屬於子視窗。子視窗的型別定義如下所示:

 public static final int FIRST_SUB_WINDOW = 1000;//子視窗型別初始值
 public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
 public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
 public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
 public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
 public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4; 
 public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
 public static final int LAST_SUB_WINDOW = 1999;//子視窗型別結束值複製程式碼

子視窗的Type值範圍為1000到1999。

系統視窗

Toast、輸入法視窗、系統音量條視窗、系統錯誤視窗都屬於系統視窗。系統視窗的型別定義如下所示:

 public static final int FIRST_SYSTEM_WINDOW     = 2000;//系統視窗型別初始值
 public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;//系統狀態列視窗
 public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;//搜尋條視窗
 public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;//通話視窗
 public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;//系統ALERT視窗
 public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;//鎖屏視窗
 public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;//TOAST視窗
 ...

 public static final int LAST_SYSTEM_WINDOW      = 2999;//系統視窗型別結束值複製程式碼

系統視窗的型別值有接近40個,這裡只列出了一小部分, 系統視窗的Type值範圍為2000到2999。

視窗顯示次序

當一個程式向WMS申請一個視窗時,WMS會為視窗確定顯示次序。為了方便視窗顯示次序的管理,手機螢幕可以虛擬的用X、Y、Z軸來表示,其中Z軸垂直於螢幕,從螢幕內指向螢幕外,這樣確定視窗顯示次序也就是確定視窗在Z軸上的次序,這個次序稱為Z-Oder。Type值是Z-Oder排序的依據,我們知道應用程式視窗的Type值範圍為1到99,子視窗1000到1999 ,系統視窗 2000到2999,,一般情況下,Type值越大則Z-Oder排序越靠前,就越靠近使用者。當然視窗顯示次序的邏輯不會這麼簡單,情況會比較多,舉個常見的情況:當多個視窗的Type值都是TYPE_APPLICATION,這時WMS會結合各種情況給出最終的Z-Oder,這個邏輯不在本文的討論範圍,這裡我們只需要知道視窗顯示次序的基本規則就好。

3.Window的標誌

Window的標誌也就是Flag,用於控制Window的顯示,同樣被定義在WindowManager的內部類LayoutParams中,一共有20多個,這裡我們給出幾個比較常用。

Flag 描述
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON 只要視窗可見,就允許在開啟狀態的螢幕上鎖屏
FLAG_NOT_FOCUSABLE 視窗不能獲得輸入焦點,設定該標誌的同時,FLAG_NOT_TOUCH_MODAL也會被設定
FLAG_NOT_TOUCHABLE 視窗不接收任何觸控事件
FLAG_NOT_TOUCH_MODAL 在該視窗區域外的觸控事件傳遞給其他的Window,而自己只會處理視窗區域內的觸控事件
FLAG_KEEP_SCREEN_ON 只要視窗可見,螢幕就會一直亮著
FLAG_LAYOUT_NO_LIMITS 允許視窗超過螢幕之外
FLAG_FULLSCREEN 隱藏所有的螢幕裝飾視窗,比如在遊戲、播放器中的全屏顯示
FLAG_SHOW_WHEN_LOCKED 視窗可以在鎖屏的視窗之上顯示
FLAG_IGNORE_CHEEK_PRESSES 當使用者的臉貼近螢幕時(比如打電話),不會去響應此事件
FLAG_TURN_SCREEN_ON 視窗顯示時將螢幕點亮

設定Window的Flag有三種方法,第一種是通過Window的addFlags方法:

Window mWindow =getWindow(); 
mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);複製程式碼

第二種通過Window的setFlags方法:

Window mWindow =getWindow();            
mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
,WindowManager.LayoutParams.FLAG_FULLSCREEN);複製程式碼

其實Window的addFlags方法內部會呼叫setFlags方法,因此這兩種方法區別不大。
第三種則是給LayoutParams設定Flag,並通過WindowManager的addView方法進行新增,如下所示。

  WindowManager.LayoutParams mWindowLayoutParams =
                new WindowManager.LayoutParams();
        mWindowLayoutParams.flags=WindowManager.LayoutParams.FLAG_FULLSCREEN;
        WindowManager mWindowManager =(WindowManager) getSystemService(Context.WINDOW_SERVICE);  
        TextView mTextView=new TextView(this);
        mWindowManager.addView(mTextView,mWindowLayoutParams);複製程式碼

4.軟鍵盤相關模式

視窗和視窗的疊加是非常常見的場景,但如果其中的視窗是軟鍵盤視窗,可能就會出現一些問題,比如典型的使用者登入介面,預設的情況彈出的軟鍵盤視窗可能會蓋住輸入框下方的按鈕,這樣使用者體驗會非常糟糕。
為了使得軟鍵盤視窗能夠按照期望來顯示,WindowManager的靜態內部類LayoutParams中定義了軟鍵盤相關模式,這裡給出常用的幾個:

SoftInputMode 描述
SOFT_INPUT_STATE_UNSPECIFIED 沒有指定狀態,系統會選擇一個合適的狀態或依賴於主題的設定
SOFT_INPUT_STATE_UNCHANGED 不會改變軟鍵盤狀態
SOFT_INPUT_STATE_HIDDEN 當使用者進入該視窗時,軟鍵盤預設隱藏
SOFT_INPUT_STATE_ALWAYS_HIDDEN 當視窗獲取焦點時,軟鍵盤總是被隱藏
SOFT_INPUT_ADJUST_RESIZE 當軟鍵盤彈出時,視窗會調整大小
SOFT_INPUT_ADJUST_RESIZE 當軟鍵盤彈出時,視窗不需要調整大小,要確保輸入焦點是可見的

從上面給出的SoftInputMode ,可以發現,它們與AndroidManifest中Activity的屬性android:windowSoftInputMode是對應的。因此,除了在AndroidMainfest中為Activity設定android:windowSoftInputMode以外還可以在Java程式碼中為Window設定SoftInputMode:

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);複製程式碼

結語

好了,就講到這裡,本篇文章講了Window的屬性,分別是Type(Window的型別)、Flag(Window的標誌)和SoftInputMode(軟鍵盤相關模式),這些知識會為後續的介紹WMS的系列文章打下基礎。本系列的下一篇會介紹WindowManager新增Window的過程,敬請期待。

參考資料
《深入理解Android 卷3》
《深入理解Android核心設計思想》第二版
《Android開發藝術探索》


我的新書《Android進階之光》已出版,更多成體系的Android相關原創技術乾貨盡在微信公眾號:劉望舒。

相關文章