關(guān)于Window你的了解妆丘?
說(shuō)到window很多同學(xué)會(huì)說(shuō)so easy,window是一個(gè)抽象類局劲,PhoneWindow是他的實(shí)現(xiàn)類勺拣,PhoneWindow中又包含decorView,這個(gè)decorVIew實(shí)際上是一個(gè)FrameLayout鱼填,decorView中又包含兩個(gè)部分药有,TitleBar和Content
如下圖所示:
Window實(shí)際上是不存在的,他是一個(gè)抽象的概念,用于View進(jìn)行依賴的愤惰。
我們setContentView來(lái)對(duì)一個(gè)activity設(shè)置xml布局文件時(shí):
1.在setContentView里面,通過getWindow獲得了當(dāng)前activity的phoneWindow窗口;
2.調(diào)用的PhoneWindow中的setContentView苇经,并在里面調(diào)用了mLayoutInflater.inflate(layoutResID, mContentParent)方法
3.將xml的布局填充到DecorView上面
我們來(lái)分析一下哈,
設(shè)置xml布局文件的目的
View view = getLayoutInflater().inflate(R.layout.layout_activity, null);
得到這樣的一個(gè)View
然后通過getWindow宦言,獲取的Window
然后通過WindowManger 來(lái)對(duì)這個(gè)Window管理扇单,比如這個(gè)Window上面依賴的View,顯示位置奠旺,透明度令花,層級(jí)
從上面的分析看的出咱們這個(gè)Window既然會(huì)被管理,那么應(yīng)該會(huì)有一些屬性進(jìn)行設(shè)置吧凉倚,是的沒錯(cuò)
咱們這個(gè)Window其實(shí)啊還是分層級(jí)的,用一個(gè)字段Z-Order表示:
1.系統(tǒng)Window
常見的系統(tǒng)Window有哪些呢兼都?比如在手機(jī)電量低的時(shí)候,會(huì)有一個(gè)提示電量低的Window,我們輸入文字的時(shí)候稽寒,會(huì)彈出輸入法Window,還有搜索條Window,來(lái)電顯示W(wǎng)indow,Toast對(duì)應(yīng)的Window,可以總結(jié)出來(lái)扮碧,系統(tǒng)Window是獨(dú)立與我們的應(yīng)用程序的,對(duì)于應(yīng)用程序而言杏糙,我們理論上是無(wú)法創(chuàng)建系統(tǒng)Window慎王,因?yàn)闆]有權(quán)限,這個(gè)權(quán)限只有系統(tǒng)進(jìn)程有宏侍。Z-Order在2000-2999
2.應(yīng)用程序Window
所謂應(yīng)用窗口指的就是該窗口對(duì)應(yīng)一個(gè)Activity赖淤,因此,要?jiǎng)?chuàng)建應(yīng)用窗口就必須在Activity中完成了谅河。本節(jié)后面會(huì)分析Activity對(duì)應(yīng)的Window的創(chuàng)建過程咱旱。Z-Order在1-99
3.子Window
所謂的子Window,是說(shuō)這個(gè)Window必須要有一個(gè)父窗體绷耍,比如PopWindow吐限,Dialog因?yàn)橛凶约旱腤indowToken,屬于應(yīng)用程序Window,這個(gè)比較特殊褂始。Z-Order在1000-1999
用一張圖來(lái)顯示就很明顯:
這里要說(shuō)明一下诸典,如果我們要把當(dāng)前這個(gè)window設(shè)置成系統(tǒng)window 那么我們就需要設(shè)置對(duì)應(yīng)的權(quán)限
要在manifest文件設(shè)置,Android6.0以上還要?jiǎng)討B(tài)獲取能在其他應(yīng)用 層上面顯示的權(quán)限
以下是常見的一些window層級(jí)屬性:
應(yīng)用級(jí)
// 應(yīng)用程序 Window 的開始值
public static final int FIRST_APPLICATION_WINDOW = 1;
// 應(yīng)用程序 Window 的基礎(chǔ)值
public static final int TYPE_BASE_APPLICATION = 1;
// 普通的應(yīng)用程序
public static final int TYPE_APPLICATION = 2;
// 特殊的應(yīng)用程序窗口,當(dāng)程序可以顯示 Window 之前使用這個(gè) Window 來(lái)顯示一些東西
public static final int TYPE_APPLICATION_STARTING = 3;
// TYPE_APPLICATION 的變體崎苗,在應(yīng)用程序顯示之前狐粱,WindowManager 會(huì)等待這個(gè) Window 繪制完畢
public static final int TYPE_DRAWN_APPLICATION = 4;
// 應(yīng)用程序 Window 的結(jié)束值
public static final int LAST_APPLICATION_WINDOW = 99;
子window級(jí)
// 子 Window 類型的開始值
public static final int FIRST_SUB_WINDOW = 1000;
// 應(yīng)用程序 Window 頂部的面板。這些 Window 出現(xiàn)在其附加 Window 的頂部胆数。
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
// 用于顯示媒體(如視頻)的 Window肌蜻。這些 Window 出現(xiàn)在其附加 Window 的后面。
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
// 應(yīng)用程序 Window 頂部的子面板幅慌。這些 Window 出現(xiàn)在其附加 Window 和任何Window的頂部
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
// 當(dāng)前Window的布局和頂級(jí)Window布局相同時(shí)宋欺,不能作為子代的容器
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
// 用顯示媒體 Window 覆蓋頂部的 Window轰豆, 這是系統(tǒng)隱藏的 API
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
// 子面板在應(yīng)用程序Window的頂部胰伍,這些Window顯示在其附加Window的頂部齿诞, 這是系統(tǒng)隱藏的 API
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
// 子 Window 類型的結(jié)束值
public static final int LAST_SUB_WINDOW = 1999;
系統(tǒng)級(jí)
// 系統(tǒng)Window類型的開始值
public static final int FIRST_SYSTEM_WINDOW = 2000;
// 系統(tǒng)狀態(tài)欄,只能有一個(gè)狀態(tài)欄骂租,它被放置在屏幕的頂部祷杈,所有其他窗口都向下移動(dòng)
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
// 系統(tǒng)搜索窗口,只能有一個(gè)搜索欄渗饮,它被放置在屏幕的頂部
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
// 已經(jīng)從系統(tǒng)中被移除但汞,可以使用 TYPE_KEYGUARD_DIALOG 代替
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
// 系統(tǒng)對(duì)話框窗口
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
// 鎖屏?xí)r顯示的對(duì)話框
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
// 輸入法窗口,位于普通 UI 之上互站,應(yīng)用程序可重新布局以免被此窗口覆蓋
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
// 輸入法對(duì)話框私蕾,顯示于當(dāng)前輸入法窗口之上
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
// 墻紙
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
// 狀態(tài)欄的滑動(dòng)面板
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
// 應(yīng)用程序疊加窗口顯示在所有窗口之上
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
// 系統(tǒng)Window類型的結(jié)束值
public static final int LAST_SYSTEM_WINDOW = 2999;
還有window的flags參數(shù)
// 當(dāng) Window 可見時(shí)允許鎖屏
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
// Window 后面的內(nèi)容都變暗
public static final int FLAG_DIM_BEHIND = 0x00000002;
// Window 不能獲得輸入焦點(diǎn),即不接受任何按鍵或按鈕事件胡桃,例如該 Window 上 有 EditView踩叭,點(diǎn)擊 EditView 是 不會(huì)彈出軟鍵盤的
// Window 范圍外的事件依舊為原窗口處理;例如點(diǎn)擊該窗口外的view翠胰,依然會(huì)有響應(yīng)容贝。另外只要設(shè)置了此Flag,都將會(huì)啟用FLAG_NOT_TOUCH_MODAL
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
// 設(shè)置了該 Flag,將 Window 之外的按鍵事件發(fā)送給后面的 Window 處理, 而自己只會(huì)處理 Window 區(qū)域內(nèi)的觸摸事件
// Window 之外的 view 也是可以響應(yīng) touch 事件之景。
public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
// 設(shè)置了該Flag斤富,表示該 Window 將不會(huì)接受任何 touch 事件,例如點(diǎn)擊該 Window 不會(huì)有響應(yīng)锻狗,只會(huì)傳給下面有聚焦的窗口满力。
public static final int FLAG_NOT_TOUCHABLE = 0x00000010;
// 只要 Window 可見時(shí)屏幕就會(huì)一直亮著
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
// 允許 Window 占滿整個(gè)屏幕
public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;
// 允許 Window 超過屏幕之外
public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200;
// 全屏顯示,隱藏所有的 Window 裝飾轻纪,比如在游戲脚囊、播放器中的全屏顯示
public static final int FLAG_FULLSCREEN = 0x00000400;
// 表示比FLAG_FULLSCREEN低一級(jí),會(huì)顯示狀態(tài)欄
public static final int FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
// 當(dāng)用戶的臉貼近屏幕時(shí)(比如打電話)桐磁,不會(huì)去響應(yīng)此事件
public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;
// 則當(dāng)按鍵動(dòng)作發(fā)生在 Window 之外時(shí)悔耘,將接收到一個(gè)MotionEvent.ACTION_OUTSIDE事件。
public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;
@Deprecated
// 窗口可以在鎖屏的 Window 之上顯示, 使用 Activity#setShowWhenLocked(boolean) 方法代替
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
// 表示負(fù)責(zé)繪制系統(tǒng)欄背景我擂。如果設(shè)置衬以,系統(tǒng)欄將以透明背景繪制,
// 此 Window 中的相應(yīng)區(qū)域?qū)⑻畛?Window#getStatusBarColor()和 Window#getNavigationBarColor()中指定的顏色校摩。
public static final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000;
// 表示要求系統(tǒng)壁紙顯示在該 Window 后面看峻,Window 表面必須是半透明的,才能真正看到它背后的壁紙
public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
window的solfInputMode屬性
這個(gè)屬性很多人都很面熟的吧衙吩,沒錯(cuò)互妓,就是跟鍵盤有關(guān)系的那個(gè)仔
// 沒有指定狀態(tài),系統(tǒng)會(huì)選擇一個(gè)合適的狀態(tài)或者依賴于主題的配置
public static final int SOFT_INPUT_STATE_UNCHANGED = 1;
// 當(dāng)用戶進(jìn)入該窗口時(shí),隱藏軟鍵盤
public static final int SOFT_INPUT_STATE_HIDDEN = 2;
// 當(dāng)窗口獲取焦點(diǎn)時(shí)冯勉,隱藏軟鍵盤
public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;
// 當(dāng)用戶進(jìn)入窗口時(shí)澈蚌,顯示軟鍵盤
public static final int SOFT_INPUT_STATE_VISIBLE = 4;
// 當(dāng)窗口獲取焦點(diǎn)時(shí),顯示軟鍵盤
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
// window會(huì)調(diào)整大小以適應(yīng)軟鍵盤窗口
public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;
// 沒有指定狀態(tài),系統(tǒng)會(huì)選擇一個(gè)合適的狀態(tài)或依賴于主題的設(shè)置
public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;
// 當(dāng)軟鍵盤彈出時(shí)灼狰,窗口會(huì)調(diào)整大小,例如點(diǎn)擊一個(gè)EditView宛瞄,整個(gè)layout都將平移可見且處于軟件盤的上方
// 同樣的該模式不能與SOFT_INPUT_ADJUST_PAN結(jié)合使用;
// 如果窗口的布局參數(shù)標(biāo)志包含F(xiàn)LAG_FULLSCREEN交胚,則將忽略這個(gè)值份汗,窗口不會(huì)調(diào)整大小,但會(huì)保持全屏蝴簇。
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
// 當(dāng)軟鍵盤彈出時(shí)杯活,窗口不需要調(diào)整大小, 要確保輸入焦點(diǎn)是可見的,
// 例如有兩個(gè)EditView的輸入框,一個(gè)為Ev1熬词,一個(gè)為Ev2轩猩,當(dāng)你點(diǎn)擊Ev1想要輸入數(shù)據(jù)時(shí),當(dāng)前的Ev1的輸入框會(huì)移到軟鍵盤上方
// 該模式不能與SOFT_INPUT_ADJUST_RESIZE結(jié)合使用
public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
// 將不會(huì)調(diào)整大小荡澎,直接覆蓋在window上
public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;
x與y屬性:指定window的位置
alpha:window的透明度
gravity:window在屏幕中的位置均践,使用的是Gravity類的常量
format:window的像素點(diǎn)格式,值定義在PixelFormat中,比如支持透明
考官再問問你WindowManager呢摩幔?
額彤委,依然可以對(duì)答入流,WindowManger嘛或衡,很明顯是一個(gè)對(duì)window進(jìn)行管理的類焦影,他是個(gè)接口類,繼承自ViewManager,那么具體的實(shí)現(xiàn)類呢封断?斯辰,慢慢開始有的同學(xué)可能就答不上來(lái)了,WindowManagerImpl坡疼,對(duì)彬呻,沒錯(cuò),那個(gè)很帥的同學(xué)柄瑰,回答對(duì)了闸氮,如果我們想要對(duì)Window進(jìn)行添加和刪除就可以使用WindowManager,具體的工作都是由WMS來(lái)處理的教沾,WindowManager和WMS通過Binder來(lái)進(jìn)行跨進(jìn)程通信蒲跨,WMS作為系統(tǒng)服務(wù)有很多API是不會(huì)暴露給WindowManager的。如下:
我們可以看到Window包含了View授翻,對(duì)View進(jìn)行管理或悲,然后WindowManger是對(duì)Window進(jìn)行管理孙咪,上面我們說(shuō)過WindowManager其實(shí)是一個(gè)接口類,那么管理的具體實(shí)現(xiàn)又是WMS去處理的巡语。這樣一來(lái)翎蹈,感覺對(duì)整體有個(gè)大的把握了,接著往下看捌臊。
剛剛說(shuō)了WindowManager是繼承自ViewManager的,我們來(lái)看看ViewManager的源碼:
package android.view;
/** Interface to let you add and remove child views to an Activity. To get an instance
* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*/
public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
很清晰兜材,ViewManger提供了三個(gè)方法理澎,addView(添加View)、updateViewLayout(更新布局)曙寡、removeView(刪除View).根據(jù)WindowManager跟ViewManger的繼承關(guān)系糠爬,WindowManger中也有這些方法,而這些方法傳入的參數(shù)都是View举庶,說(shuō)明WindowManager具體管理的是以View形式存在的Window执隧,當(dāng)然通過源碼中可以看到Windowmanger作為一個(gè)子類,其中還有很多自己的常量跟方法户侥,這里就不一一貼出來(lái)了镀琉,后面會(huì)說(shuō)到額。
那么實(shí)際應(yīng)用的過程中蕊唐,我們是怎么來(lái)使用的呢屋摔?
比如我們現(xiàn)在通過在window添加view的方式,來(lái)在界面上面顯示一個(gè)按鈕替梨,而不是通過Activity中的setContentView方法去實(shí)現(xiàn)钓试。
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button floatingButton = new Button(this);
floatingButton.setText("button");
//通過windowmanager.layoutparams來(lái)設(shè)置window的相關(guān)屬性
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
0, 0,
PixelFormat.TRANSPARENT
);
// flag 設(shè)置 Window 屬性
layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
// type 設(shè)置 Window 類別(層級(jí)),這里需要應(yīng)用上層顯示權(quán)限(6.0及以上)
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
//設(shè)置window顯示的位置
layoutParams.gravity = Gravity.CENTER;
WindowManager windowManager = getWindowManager();
//通過windowmanger來(lái)添加view
windowManager.addView(floatingButton, layoutParams);
}
//這里給出的一個(gè)應(yīng)用Window 相當(dāng)于Activity的界面顯示副瀑,咱們不用setCOntentView這個(gè)方法
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,TYPE_APPLICATION,0, PixelFormat.TRANSPARENT);
layoutParams.gravity = Gravity.CENTER;
layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
layoutParams.type = TYPE_APPLICATION;
WindowManager windowManager = getWindowManager();
View view = LayoutInflater.from(this).inflate(R.layout.activity_demo6, null);
windowManager.addView(view,layoutParams);
很清晰的可以看到弓熏,我們是通過windowManger來(lái)設(shè)置了window的相關(guān)屬性,然后把view添加到window上去的糠睡,雖然全程并沒有看到關(guān)于window的對(duì)象.當(dāng)然這里提高一點(diǎn)挽鞠,我們?nèi)绻亲远x的一些View,比如繼承自Dialog的時(shí)候也可以這么實(shí)現(xiàn):
private void initView() {
View view = getLayoutInflater().inflate(R.layout.test, null);
//findView過程
initFindView(view);
// 此處是直接調(diào)用的Dialog的setContentView方法
setContentView(view);
initAttribute();
Window window = getWindow();
window.setGravity(Gravity.BOTTOM);
// 設(shè)置dialog占滿父容器
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
window.setAttributes(lp);
// 設(shè)置點(diǎn)擊外圍消散
setCanceledOnTouchOutside(true);
}
//Window中的方法
public void setAttributes(WindowManager.LayoutParams a) {
mWindowAttributes.copyFrom(a);
dispatchWindowAttributesChanged(mWindowAttributes);
}
實(shí)際處理過程如下圖:
繼續(xù)來(lái)深入分析一波:
看似是通過WindowManager來(lái)addView狈孔,但是上面已經(jīng)說(shuō)過了WindowManger是個(gè)接口類滞谢,具體的add到底是哪里呢?它的真正實(shí)現(xiàn)是WindowManagerImpl類除抛。
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
接著往下看WindowManagerGlobal中的addView方法做了什么
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
……
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
// 界面布局的限制
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
……
// 構(gòu)建界面控制
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//將View顯示到手機(jī)窗口
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
ViewRootImpl 是一個(gè)視圖層次結(jié)構(gòu)的頂部狮杨,ViewRootImpl 實(shí)現(xiàn)了 View 與 WindowManager 之間所需要的協(xié)議,作為 WindowManagerGlobal 中大部分的內(nèi)部實(shí)現(xiàn)到忽。
參考文獻(xiàn):https://blog.csdn.net/weixin_43766753/article/details/108350589
http://www.reibang.com/p/1973b529a414
http://www.reibang.com/p/c5c3ef2b1b03