天天看點

Android好奇寶寶_05_PopupWindow與懸浮窗

這一篇講講PopupWindow與懸浮窗之間那些不得不說的故事。

之是以把PopupWindow與懸浮窗這兩個放到一起講,是因為這兩個的實作原理基本是一緻的,隻是有點不同而已。

原理:

使用系統服務(WindowManagerService)将要顯示的View添加進Window中。

WindowManagerService和ActivityManagerService是Android系統中兩個最重要的服務,其中一個管理視窗顯示,一個管理四大元件。

ActivityManagerService這裡呢先不講,以後再說。

簡單講下WindowManagerService,想深入了解的建議看下老羅大大的部落格,新手慎入:傳送門

以一個最簡單的HelloWorld來說明:

當Activity被啟動時,Activity會被配置設定一個Window,一個window代表一個充滿螢幕的視窗。然後當我們調用setContentView時,我們要顯示的View(後面用contentView表示)就會被添加進該Activity的window中。window接收到contentView後會根據設定在其外部包裹一些其它view(比如titlebar),然後請求WindowManagerService,WindowManagerService則向底層一層一層發送請求直到硬體将其顯示出來。

上面的說明簡化了很多過程,因為不是我想講的重點,這一篇隻是想簡單講下怎麼用WindowManagerService,而不是它的實作原理(原理我也講不來。。)。

一般我們添加View的方式是将子view添加到一個已經存在于window上的父view中。但其實利用WindowManagerService我們是可以直接将view添加的window中的,就像系統幫我們自動将setContentView的contentView添加到window一樣。

兩種方式的主要差別在于子view添加進父view,那麼子view和父view就位于同一個window,但用WindowManagerService添加view,我們可以指定不同window(如果你有足夠權限的話),我這裡說的不同window是指他們的等級不同。PopupWindow依附于Activity,屬于應用級。而懸浮窗就是屬于系統級,是以它才可以保持在前端顯示(其它系統級的window還有來電提醒,ANR、FC等系統警告等)。

再說說WindowManagerService和WindowManager的差別:

WindowManagerService是系統服務,當并不完全對開發者開放,而是使用WindowManager做為代理,提供部分功能和包裝後的功能,可以說WindowManager是閹割版的WindowManagerService,那些會危害系統安全的功能被屏蔽了。Android系統中的系統服務基本都是這種方式。

說完原理來看看代碼證明一下,先看下系統實作的PopupWindow,在根據原理來實作一個懸浮窗。

PopupWindow:

其實PopupWindow裡面隻有兩句核心的代碼,其它的都是一些位置和大小的計算。

(1)在構造方法中,通過傳入的context或則view獲得WindowManager:

mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
           

或者:

mContext = contentView.getContext();
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
           

(2)通過 WindowManager将PopupWindow添加進window:

mWindowManager.addView(mPopupView, p);
           

是不是炒雞簡單?

那麼我們來開始實作懸浮窗:

(1)先擷取WindowManager:

mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
           

注意這裡要使用Application的context來擷取WindowManager,如果使用activity.getSystemService(Context.WINDOW_SERVICE)來擷取的話,得到的WindowManager的生命周期是小于等于Activity的,而使用上面這種方式得到的WindowManager是跟整個應用一樣的,這樣除非你的整個應用程序都被終結了,不然你的懸浮窗就可以一直顯示。

(2)設定參數WindowManager.LayoutParams:

大小位置等可以根據需求來設定,但是要記住設定顯示的級别:

wmParams.type = LayoutParams.TYPE_PHONE;
           

(3)将要顯示的view添加進window:

mWindowManager.addView(floatView, wmParams);
           

當然你也可以對floatView和其子view進行點選、觸摸等事件的監聽,然後可以通過 WindowManager的另一個方法來重新整理floatView:

mWindowManager.updateViewLayout(floatView, wmParams);
           

舉個栗子:

你可以監聽觸摸事件,得到觸摸位置并設定進wmParams裡,再調用updateViewLayout方法來實作懸浮窗随手指移動的效果。

一點補充:網上的懸浮窗demo很多都是在Service中生成的,其實在Activity生成是一樣的。但Service的有個好處是可以讓應用程序不那麼容易被殺死。

樓主寫的一個demo效果圖:

Android好奇寶寶_05_PopupWindow與懸浮窗

這裡的吃豆人是我再另一篇博文寫得demo摘出來的,沒啥好說的,有興趣的可以去支援下:

一個有吃豆人删除動畫的ListView

本篇Demo下載下傳

終于擺脫每一篇都是講AbsListView的厄運了。

求贊求評論