android懸浮窗的實現

懸浮窗原理

思路:
1.建立一個服務,並且在裏面生成一個WindowManager對象,通過它來加載一個視圖作爲懸浮窗。
2.設置WindowManager的參數LayoutParams
3.設置一個容器來找到懸浮窗的父控件,並綁定到windowManager中去
4.通過父控件來加載懸浮窗的視圖
這個思路需要注意的兩個細節:
a. WindowManager的參數LayoutParams中的type字段,目前主流的做法是將改字段賦值爲LayoutParams.TYPE_PHONE.
b. 需要在AndroidManifest中加上相應的權限

否則無法出現懸浮窗。
上面省略的佈局文件的說明,詳情可以參考代碼部分。
到這裏,如果運氣好的話,你很容易就可以用代碼實現一個懸浮窗,再複雜一點的還可以順便實現拖動效果,然而現實總是很殘酷的,由於安卓系統的碎片化現象太嚴重,各家ROM對這個懸浮窗做了各種限制,比如小米的miui、魅族和錘子等只有用戶進入到應用設置頁面,打開懸浮窗開關才能顯示懸浮窗(如圖1和圖2),這就比較麻煩了,從而導致早期懸浮窗在實際中比較難以推廣。
這裏寫圖片描述
圖 1 錘子系統
這裏寫圖片描述
圖 2 MIUI系統

後來發現安卓本身有一些特殊的方式可以繞過這個權限檢查,即將LayoutParams的type值設置成LayoutParams.TYPE_TOAST,如此一來開發者不需要再在AndroidManifest中添加權限,還可以使得App避開第三方ROM的權限檢查,無需開啓開關就能顯示出懸浮窗,至此世界很美好。
但是,TYPE_TOAST也是有本身的侷限性的,即從4.4以後的版本開始,使用TYPE_TOAST方式顯示出來的懸浮窗能接收觸摸事件和按鍵事件,而4.4以前只能顯示出來,不能交互
可以通過附件中的demo體驗一下懸浮窗效果,增加了觸摸和點擊事件。

效果圖:
這裏寫圖片描述
圖 3
本文中借鑑了其他文章的相關資料
下面是啓動service時執行的顯示懸浮窗的主要代碼

/**
* 初始化windowManager
*/
private void initWindow(){
wmParams = new WindowManager.LayoutParams();
//獲取的是WindowManagerImpl.CompatModeWrapper
mWindowManager = (WindowManager)getApplication().getSystemService(getApplication().WINDOW_SERVICE);
//設置window type 下面變量2002是在屏幕區域顯示,2003則可以顯示在狀態欄之上
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;//無需權限
//wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;//需要權限,且在某些系統中還需要手動打開設置,比如miui
//設置圖片格式,效果爲背景透明
wmParams.format = PixelFormat.RGBA_8888;
//設置浮動窗口不可聚焦(實現操作除浮動窗口外的其他可見窗口的操作)
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
//調整懸浮窗顯示的停靠位置爲左側置頂
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
// 以屏幕左上角爲原點,設置xy初始值,相對於gravity
wmParams.x = 0;wmParams.y = 0;
//設置懸浮窗口長寬數據
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
LayoutInflater inflater = LayoutInflater.from(getApplication());
//獲取浮動窗口視圖所在佈局
mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);
//添加mFloatLayout
mWindowManager.addView(mFloatLayout, wmParams);
//浮動窗口按鈕
mFloatView = (Button)mFloatLayout.findViewById(R.id.float_id);    mFloatLayout.measure(View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); 
}

順便提一下在在小米系統中,如果使用TYPE_PHONE方式實現懸浮窗,則最好先判斷是否打開了顯示懸浮窗的開關,如果沒有打開,則跳轉到應用設置詳情頁面,跳轉方法如下:

Uri packageURI = Uri.parse("package:" + "你的應用包名");  
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);  
startActivity(intent);  

源碼下載:android實現懸浮窗功能

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章