
打開簡信我的首頁,亮閃閃的幾個(gè)字6天之前,是的,距離我的上一篇文章已經(jīng)6天,原計(jì)劃這篇文章會是在周一晚上的時(shí)候發(fā)的,可是最近也有點(diǎn)突發(fā)事件,打亂我原有的計(jì)劃???!反正就是瞎忙,不過我還是會堅(jiān)持用心寫好每一篇文章。
記得上一篇講的是用RotateDrawable實(shí)現(xiàn)網(wǎng)易云音樂唱片機(jī)效果,而今天我要講的是如何用WindowManager去實(shí)現(xiàn)一個(gè)懸浮窗迷你音樂盒。,由于WindowManager這一塊內(nèi)容會比較多,所以我決定分成上下兩篇來寫這一塊,這一篇主要介紹WindowManager和它的一些屬性,可能會有些枯燥,下一篇我會具體去寫懸浮迷你音樂盒的實(shí)現(xiàn)。
WindowManager
在Android應(yīng)用開發(fā)中,其實(shí)整個(gè)Android的窗口機(jī)制是基于一個(gè)叫做WindowManager的一個(gè)系統(tǒng)服務(wù)接口,WindowManager可以添加view到屏幕,也可以從屏幕刪除view。它面向的對象一端是屏幕,另一端就是View,其實(shí)就連我們常用的Activity和Diolog的底層實(shí)現(xiàn)都是通過WindowManager, WindowManager是全局的,整個(gè)系統(tǒng)就只用一個(gè)Windowmanager服務(wù),我們需要向系統(tǒng)獲取服務(wù)才能調(diào)用它,而它就是顯示View的最底層。
其實(shí)WindowManager用起來非常方便,就三個(gè)方法:
添加View
addView(View view, WindowManager.LayoutParams params);
從方法中我們可以看到,addView需要兩個(gè)參數(shù),view簡單,就是我們要向窗口中去添加的對象,至于params,就是給窗口設(shè)置的顯示策略,包括窗口的大小、透明度等等,這個(gè)也是今天文章的重點(diǎn),在后文會有所介紹。
移除View
removeView(View view);
既然能夠向窗口去添加View,當(dāng)然也就能夠從窗口上移除View,這個(gè)很簡單view就是你要從窗口中移除的對象。
刷新View
updateViewLayout(View view, ViewGroup.LayoutParams params)
同樣窗口刷新也需要兩個(gè)參數(shù),和添加View一樣view是需要更新的對象,而params就是更新后的策略屬性。
WindowManager.LayoutParams
相比于WindowManager,WindowManager.LayoutParams可就要復(fù)雜好多了。WindowManager.LayoutParams是 WindowManager 接口的嵌套類,在窗口管理中扮演著重要的角色。它繼承于ViewGroup.LayoutParams,它用于向WindowManager描述窗口的管理策略;WindowManager.LayoutParams可以直接new WindowManager.LayoutParams()新建,也可以從對窗口的getAttributes()得到其WindowManager.LayoutParams對象。WindowManager.LayoutParams常用的有以下主要常量成員:
flag
-
WindowManager.LayoutParams.FLAG_SECURE不允許截屏;設(shè)置了這個(gè)屬性的窗口,在窗口可見的情況下,是會禁用系統(tǒng)的截圖功能的。那么問題來了:假如有一天,你的公司要求寫一個(gè)類似于‘閱后即焚’功能的頁面的話,不妨在activity中獲得WindowManager.LayoutParams并添加該屬性,輕輕松松搞定。 -
WindowManager.LayoutParams.FLAG_BLUR_BEHIND背景模糊;假如你的窗口設(shè)置了這個(gè)屬性,并且這個(gè)窗口可見,在這窗口之后的所有背景都會被模糊化,但我還沒有發(fā)現(xiàn)一個(gè)屬性是可以控制模糊程度的。 -
WindowManager.LayoutParams.FLAG_DIM_BEHIND背景變暗;設(shè)置這個(gè)效果的窗口,在窗口可見的情況下,窗口后方的背景會相應(yīng)的變暗,這個(gè)屬性需要配合參數(shù)dimAmount一起使用,dimAmount會在后文中介紹。 -
WindowManager.LayoutParams.FLAG_FULLSCREEN設(shè)置全屏;這個(gè)屬性也許是大家接觸的最多的一個(gè)屬性,很多應(yīng)用開發(fā)過程中會要求有些頁面需要動態(tài)設(shè)置Activity為全屏,而我們只需要獲得Activity的WindowManager.LayoutParams并設(shè)置WindowManager.LayoutParams.FLAG_FULLSCREEN屬性就行。 -
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON設(shè)備常亮;設(shè)置這個(gè)屬性的窗口,在窗口可見的情況下,整個(gè)屏幕會處于常亮并且高亮度的狀態(tài),并且不受待機(jī)時(shí)間的約束。 -
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS布局不受限制;設(shè)置這個(gè)屬性的窗口,將不再受設(shè)備顯示范圍邊界 的約束,通俗點(diǎn)講,就是窗口可以出設(shè)備之外,然后移除部分不可見。具體會在坐標(biāo)參數(shù)中講到。 -
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE不設(shè)置聚焦;關(guān)于焦點(diǎn)獲得我有必要說明一下,如果窗口獲得焦點(diǎn)的話,只要窗口處于可視化狀態(tài),當(dāng)前設(shè)備的物理按鍵點(diǎn)擊事件都會被這個(gè)窗口接收,但是如果不設(shè)置窗口的焦點(diǎn)的話,直接傳遞到之后窗口進(jìn)行接收。這就導(dǎo)致一個(gè)問題,如果你的需求要求你寫的懸浮窗點(diǎn)擊返回鍵能夠關(guān)閉或是進(jìn)行其他操作的話,你就必須讓你的窗口獲得焦點(diǎn),并為當(dāng)前View設(shè)置按鍵監(jiān)聽事件。 -
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE取消觸摸事件; 設(shè)置這個(gè)屬性的窗口將不再處理任何Touch事件,就算顯示的View設(shè)置了onTouch事件,那么這個(gè)窗口就會是一個(gè)僵尸窗口。 -
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL不知道怎么去歸納,這個(gè)屬性還是比較有意思的,設(shè)置這個(gè)屬性的窗口,在窗口可見的情況下,就算窗口沒有設(shè)置屬性FLAG_NOT_FOCUSABLE,也就是在窗口獲得焦點(diǎn)的情況下,當(dāng)觸摸事件是在窗口之外區(qū)域的時(shí)候,窗口不在攔截觸摸事件,而是將事件往下傳遞,也算是解決聚焦后的事件攔截問題吧。 -
WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER顯示壁紙;官方文檔說明是在窗口之后顯示系統(tǒng)壁紙,但是我親測,似乎并沒有這個(gè)想效果,還是這個(gè)屬性需要配合其他的屬性設(shè)置一起使用,希望有設(shè)置成功的小伙伴能夠在評論區(qū)分享你的結(jié)果。 -
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED鎖屏顯示;關(guān)于這個(gè)屬性官方文檔給出的說明是在鎖屏的時(shí)候顯示的窗口,但是,實(shí)在慚愧,在下還是沒有能夠有一個(gè)實(shí)驗(yàn)結(jié)果,不知道是需要給權(quán)限呢還是需要同時(shí)進(jìn)行其他設(shè)置。同樣,還是很希望有知道的小伙伴能夠在評論區(qū)向大家分享。 -
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON點(diǎn)亮屏幕;設(shè)置這個(gè)屬性的窗口,當(dāng)窗口顯示的時(shí)候,如果設(shè)備處于待機(jī)狀態(tài),會點(diǎn)亮設(shè)備。這個(gè)應(yīng)該在很多鎖屏窗口中用的比較多,比如收到消息點(diǎn)亮屏幕。 -
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH這個(gè)也不知道怎么去歸納,也是一個(gè)比較有意思的屬性,之前我們說到FLAG_NOT_TOUCH_MODAL,在窗口獲得焦點(diǎn)的情況下,當(dāng)觸摸事件是在窗口之外區(qū)域的時(shí)候,窗口不在攔截觸摸事件,而是將事件往下傳遞,而如果再設(shè)置這個(gè)屬性,窗口能在MotionEvent.ACTION_OUTSIDE中收獲窗口之外的點(diǎn)擊事件,遺憾的是不能進(jìn)行屏蔽,也就是說事件依然會向下傳遞。
以上的也是最常用到的幾個(gè)flag屬性了吧,其他還有很多,也希望大家空閑之余能夠去研究研究,歡迎再評論區(qū)補(bǔ)充。
type
type主要用于表示window的類型。我們可以通過WindowManager.LayoutParams的type變量對窗口類型直接進(jìn)行設(shè)置。常用的窗口類型也就以下兩種:
-
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL我在之前文章中介紹過的PopupWindow,我也翻閱過PopupWindow的源碼,PopupWindow用的就是TYPE_APPLICATION_PANEL這個(gè)屬性類型。這種類型的窗口在顯示寄生于宿主窗口,并顯示與宿主窗口之上,因此這種類型的窗口會隨著宿主窗口的關(guān)閉而關(guān)閉,顯然不能滿足我們懸浮窗的要求。 -
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT系統(tǒng)提示窗口,常見的比如內(nèi)存不夠的警告、低電量警告。它總是出現(xiàn)在應(yīng)用程序窗口之上,而這一點(diǎn),正合我們做一個(gè)能夠顯示在任何應(yīng)用之上的懸浮迷你音樂盒的要求。
screenBrightness、buttonBrightness
其中screenBrightness表示屏幕的亮度,而buttonBrightness表示一般按鍵和鍵盤按鍵的亮度。它們都擁有以下三個(gè)系統(tǒng)屬性:
-
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF最低屏幕亮度。 -
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE默認(rèn)屏幕亮度。 -
WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL最高屏幕亮度。
dimAmount
講flag屬性的時(shí)候有提到過,這個(gè)參數(shù)是要和WindowManager.LayoutParams.FLAG_DIM_BEHIND這個(gè)flag屬性一起使用,dimAmount的取值在0.0f~1.0f之間,取值越大背景的變暗程度越高,默認(rèn)取值1.0f。
width、height
這里的width、height其實(shí)和View中的width、height一樣的理解,就是控制窗口視圖的大小,可以具體取值,也可以使用系統(tǒng)屬性:
-
WindowManager.LayoutParams.WRAP_CONTENT自適應(yīng)大小 -
WindowManager.LayoutParams.MATCH_PARENT填滿整個(gè)布局
gravity
窗口的對齊方式,一般在創(chuàng)建窗口的時(shí)候,都會設(shè)置gravity為左上角對齊,也就是Gravity.LEFT | Gravity.TOP,因?yàn)榇翱诘淖鴺?biāo)設(shè)置,是基于gravity來進(jìn)行計(jì)算的,設(shè)置gravity左上角,剛好是和系統(tǒng)的坐標(biāo)相對應(yīng),方便計(jì)算。
x、y
x和y用于控制窗口的坐標(biāo)位置,如果有設(shè)置gravity的話,x和y設(shè)置的就是在gravity這個(gè)基礎(chǔ)上的一個(gè)偏移量。不設(shè)置gravity的話,x和y就是一個(gè)絕對坐標(biāo)。因此,將gravity設(shè)置為Gravity.LEFT | Gravity.TOP是最易于開發(fā)的。需要注意的一點(diǎn)是:設(shè)置y的時(shí)候常常需要考慮狀態(tài)欄的高度。
正常情況下,就算x和y的坐標(biāo)已經(jīng)在設(shè)備之外,也會貼邊顯示。而如果設(shè)置屬性FLAG_LAYOUT_NO_LIMITS則相對于系統(tǒng)的坐標(biāo)如果x和y超出設(shè)備,那么超出部分將無法顯示。
windowAnimations
windowAnimations控制的是窗口出現(xiàn)和消失的動畫效果,設(shè)置的是要系統(tǒng)自帶的動畫效果(android.R.style之下的動畫效果),因?yàn)榇翱诠芾砥魇遣荒茉L問應(yīng)用資源的。
format
format可以理解為最后窗口生成的位圖是什么格式,默認(rèn)背景是黑色的。一般我們都設(shè)置為PixelFormat.RGBA_8888,這樣我們的窗口就會有一個(gè)透明的背景。
alpha
這個(gè)不難理解,設(shè)置窗口的透明度。
其實(shí)WindowManager.LayoutParams的屬性有很多,全介紹一遍恐怕要講到天亮,而且還有一些我本人也沒有試過,要是還有什么比較實(shí)用或是比較有趣的屬性,也歡迎小伙伴們在評論區(qū)留言??! 不勝感激?。。?!
下篇預(yù)告:像360懸浮窗那樣,用WindowManager實(shí)現(xiàn)炫酷的懸浮迷你音樂盒(下),會有意想不到的驚喜。
如果文中有表述不當(dāng)或闡述錯(cuò)誤的地方,還望正在看文章的您可以幫忙指出,有疑惑也可以在評論區(qū)提問或者私信,期待您的意見和建議,歡迎關(guān)注交流。