為了分析Window的工作機(jī)制,我們需要先了解如何使用WindowManager添加一個(gè)Window,下面的代碼演示了通過(guò)WindowManager添加Window的過(guò)程
Button mFloatingButton =new Button(this);
mFloatingButton.setText("button");
int type = WindowManager.LayoutParams.TYPE_TOAST;
int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
WindowManager.LayoutParams mLayoutParams = new
WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,type ,flags , PixelFormat.TRANSPARENT);
mLayoutParams.gravity = Gravity.LEFT|Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
WindowManager mWindowManager = this.getWindowManager();
mWindowManager.addView(mFloatingButton,mLayoutParams);
上述代碼可以將一個(gè)Button添加到屏幕坐標(biāo)為(100,300)的位置上。WindowManager.LayoutParams中的flags和type這兩個(gè)參數(shù)比較重要,下面對(duì)其進(jìn)行說(shuō)明。
Flags參數(shù)表示W(wǎng)indow的屬性,它有很多選項(xiàng),通過(guò)這些選項(xiàng)可以控制Window的顯示特性,這里介紹幾個(gè)常用的選項(xiàng):
- FLAG_NOT_FOCUSABLE
表示W(wǎng)indow不需要獲取焦點(diǎn),也不需要接收各種輸入事件,此標(biāo)記會(huì)同時(shí)啟用FLAG_NOT_TOUCH_MODAL,最終事件會(huì)直接傳遞給下層的具有焦點(diǎn)的Window。- FLAG_NOT_TOUCH_MODAL
系統(tǒng)會(huì)將當(dāng)前Window區(qū)域以外的單擊事件傳遞給底層的Window,當(dāng)前Window區(qū)域內(nèi)的單擊事件則自己處理。這個(gè)標(biāo)記很重要,一般來(lái)說(shuō)都需要開(kāi)啟此標(biāo)記。否則其他Window將無(wú)法收到單擊事件。- FLAG_SHOW_WHEN_LOCKED
開(kāi)啟此模式可以讓W(xué)indow顯示在鎖屏的界面上。
Type參數(shù)表示W(wǎng)indow的類型,Window有三種類型,分別是應(yīng)用Window、子Window和系統(tǒng)Window。應(yīng)用類Window對(duì)應(yīng)一個(gè)Activity。子Window不能單獨(dú)存在,它需要附屬在特定的父Window之中,比如常見(jiàn)的一些Dialog就是一個(gè)子Window。系統(tǒng)Window是需要聲明權(quán)限在能創(chuàng)建的Window,比如Toast和系統(tǒng)狀態(tài)欄這些都是系統(tǒng)Window。
Window是分層的,每個(gè)Window都有對(duì)應(yīng)的z-ordered,層級(jí)大的會(huì)覆蓋在層級(jí)小的Window的上面,這和HTML中的z-index的概念是完全一致的。在三類Window中,應(yīng)用Window的層級(jí)范圍是199,子Window的層級(jí)范圍是10001999,系統(tǒng)Window的層級(jí)范圍是2000~2999,這些層級(jí)范圍對(duì)應(yīng)著WindowManager.LayoutParams的type參數(shù)。如果想要Window位于所有Window的最頂層,那么采用較大的層級(jí)即可。很顯然系統(tǒng)Window的層級(jí)是最大的,而且系統(tǒng)層級(jí)有很多值,一般我們可以選用TYPE_SYSTEM_OVERLAY或者TYPE_SYSTEM_ERROR,如果 采用TYPE_SYSTEM_ERROR,只需要為type參數(shù)指定這個(gè)層級(jí)即可:mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;同時(shí)聲明權(quán)限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW“>。
因?yàn)橄到y(tǒng)類型的Window是需要檢查權(quán)限的,如果不在AndroidManifest中使用相應(yīng)的權(quán)限,那么創(chuàng)建Window的時(shí)候就會(huì)報(bào)錯(cuò)。
WindowManager所提供的功能很簡(jiǎn)單,常用的只要三個(gè)方法 ,即添加View、更新View和刪除View,這三個(gè)方法定義在ViewManager中,而WindowManager繼承了ViewManager。
public interface ViewManager{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
Window是一個(gè)抽象概念,每一個(gè)Window都對(duì)應(yīng)著一個(gè)View和一個(gè)ViewRootImpl,Window和View通過(guò)ViewRootImpl來(lái)建立聯(lián)系,因此Window并不是實(shí)際存在的,它是以View的是形式存在。這點(diǎn)可以從WindowManager的定義中看出來(lái),它提供的三個(gè)接口方法addView、updateViewLayout以及removeView都是針對(duì)View的,這說(shuō)明View才是Window存在的實(shí)體,有視圖的地方就有Window,比如Activity、Dialog、Toast,除此之外,還有一些依托Window而實(shí)現(xiàn)的視圖,比如PopUpWindow、菜單。在實(shí)際使用中無(wú)法直接訪問(wèn)Window,必須通過(guò)WindowManager。