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