前言
一般的懸浮窗實(shí)現(xiàn)方式,需要申請(qǐng)權(quán)限,并還是要對(duì)部分機(jī)型進(jìn)行適配才能正常顯示。那么這里,我們換一種思路,實(shí)現(xiàn)一個(gè)不一樣的懸浮窗。
一、應(yīng)用內(nèi)懸浮窗實(shí)現(xiàn)思路
通常的懸浮窗是通過(guò)WindowManager直接添加的,在不同的Android系統(tǒng)上需要做不同的適配,在Android6.0以上的機(jī)型上,還需要引導(dǎo)用戶(hù)跳轉(zhuǎn)到設(shè)置界面手動(dòng)開(kāi)啟懸浮窗權(quán)限。雖然這樣實(shí)現(xiàn)懸浮窗有完整的解決方案,但是開(kāi)啟懸浮窗過(guò)程對(duì)用戶(hù)并不是很友好。下面,我們換一種思路,去使用一個(gè)應(yīng)用內(nèi)懸浮窗,避免機(jī)型適配和權(quán)限申請(qǐng)的坑,讓?xiě)腋〈跋衿胀ǖ?code>View一樣顯示在界面上。
一般懸浮窗的實(shí)現(xiàn)方案是向系統(tǒng)window添加type為TYPE_PHONE或者TYPE_TOAST的View,從而使懸浮窗可以作為一個(gè)獨(dú)立的View進(jìn)行展示。Android對(duì)這一行為作了限制,那我們可以考慮從比較常規(guī)的途徑添加View:向每一個(gè)展示界面,即Activity,添加一個(gè)View作為懸浮窗。這樣,我們使用懸浮窗時(shí)就可以避免適配和權(quán)限問(wèn)題。那么,怎么樣實(shí)現(xiàn)這樣的懸浮窗更好呢?
要實(shí)現(xiàn)這樣一個(gè)懸浮窗,相當(dāng)于我們要在Activity加載完后將懸浮窗的View添加的Activity上,我們不想在原有的Activity上插入這段代碼,這時(shí)就可以利用ActivityLifecycleCallbacks和fragment的加載特性來(lái)完成一個(gè)無(wú)侵入式的懸浮窗的顯示。

二、應(yīng)用內(nèi)懸浮窗的實(shí)現(xiàn)
- 首先,我們先自定義一個(gè)
View用于顯示懸浮窗界面,就叫它FloatingWindow。至于怎么實(shí)現(xiàn),這個(gè)各位可以自由發(fā)揮。 - 接下來(lái),我們要把
FloatingWindow添加到每一個(gè)Activity上,這時(shí)就利用ActivityLifecycleCallbacks。Activity的每個(gè)生命周期都能回調(diào)到ActivityLifecycleCallbacks,這時(shí)我們只要在onActivityCreated(Activity activity, Bundle savedInstanceState)中加上懸浮窗View。但是,onActivityCreated(Activity activity, Bundle savedInstanceState)方法是在onCreate(Bundle savedInstanceState)時(shí)被調(diào)用的,我們需要保證在setContentView()之后才添加懸浮窗,讓?xiě)腋〈疤幱谏蠈?,所以我們插入一個(gè)空Fragment,利用Fragment的onActivityCreated(Bundle savedInstanceState)是在Activity的onCreate(Bundle savedInstanceState)之后的特性來(lái)加入懸浮窗。
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
super.onActivityCreated(activity, savedInstanceState);
if (activity instanceof FragmentActivity) {
FragmentManager fm = ((FragmentActivity) activity).getSupportFragmentManager();
fm.beginTransaction().add(new SupportFragment(), FRAGMENT_TAG).commitAllowingStateLoss();
}
}
...
});
public static class SupportFragment extends Fragment {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Activity activity = getActivity();
if (activity != null) {
FloatingWindow fw = new FloatingWindow(activity);
activity.addContentView(fw, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
}
通過(guò)這幾行核心代碼,我們便完成了一個(gè)不需要權(quán)限申請(qǐng)的懸浮窗。細(xì)節(jié)一點(diǎn)的實(shí)現(xiàn)可以參考我的demo:https://github.com/windinwork/floatingwindowdemo
三、總結(jié)
像我們這樣的懸浮窗,有優(yōu)點(diǎn)也有缺點(diǎn)。優(yōu)點(diǎn)顯而易見(jiàn),它不需要向系統(tǒng)申請(qǐng)?zhí)厥獾臋?quán)限即可正常顯示;缺點(diǎn)的話(huà)即是每一個(gè)Activity都有一個(gè)懸浮窗,相互獨(dú)立存在,當(dāng)然這個(gè)是可以?xún)?yōu)化一下實(shí)現(xiàn)方式解決的,這里不細(xì)講,另一個(gè)缺點(diǎn)即是這樣的懸浮窗無(wú)法在應(yīng)用退到后臺(tái)的時(shí)候存在,當(dāng)然在在合適的應(yīng)用場(chǎng)景這也不是問(wèn)題。以上便是一個(gè)無(wú)侵入式無(wú)權(quán)限的懸浮窗實(shí)現(xiàn)方式,希望能為小伙伴提供不同的懸浮窗實(shí)現(xiàn)思路。