剛剛發了一個大版本,終於有時間來總結一下技術點了,今天就來總結一下上個版本用到的懸浮窗吧,類似於微信公眾號文章裡的效果,可以拖動可以點選,拖動以後根據拖動的位置顯示不同的懸浮窗圖片,具備左右自動吸頂效果,當前app不在前臺時會自動隱藏。目前已經順利上線,很多問題已經解決,線上並沒有出現大的問題。今天我想把自己這次使用懸浮窗的方法以及其中的坑點告訴大家,希望能對大家有所幫助,碰到問題也可以一起討論,歡迎小夥伴們一起進步,come on快上車。
最開始接到懸浮窗的需求也是有點虛的,因為之前沒有做過這方面,只是聽說過這個比較坑,等做完才發現這個是真的坑。廢話少說,直接進入主題,首先從我調研這個需求說起吧。遇到沒做過的需求,習慣性地去往github上搜尋懸浮窗相關的第三方庫,希望能站在巨人的肩膀上。不出所以然,我找到了一個第三方git庫:github.com/yhaolpz/Flo…,我下載了demo跑了一遍,發現還不錯,支援滑動事件和點選事件,可以設定大小,可以設定懸浮窗的顯隱。心裡樂滋滋地準備往自己專案裡搬運,可是剛想搬的時候發現就不對勁了,我們專案是在需要的時候再申請許可權,而不是開啟app就申請許可權並顯示。於是我一通亂改,由於對這一套不瞭解,改完發現越改越亂bug越多,心態已經崩了。這個時候突然靈光一閃,先去issue裡找找看有沒有人碰到同樣的問題,果然是船到橋頭自然直,還真讓我找到了。有個大神碰到相同的問題並且已經解決了,fork了程式碼並且進行了修改,這個git庫是:github.com/baneyue/Flo…,在此非常感謝前輩們的付出,站在巨人的肩膀上的感覺就是如此奇妙,自己也要多出一些技術部落格分享給別人,因為每個人做的技術需求都不大相同,應用場景不一樣也會導致遇到的Bug不經相同,要取長補短,哈哈。
安卓的懸浮窗還是比較坑的,最坑的是許可權,其次沒有成熟的官方庫來做這個事情,導致很多開發者自己去做或多或少地都有不同的問題,再次懸浮窗是window,本身谷歌提供的api就很難使用,比如只能add不能replace,並且多次add還會導致crash,這種奇葩的為難開發者的設計真是毀三觀啊。吐槽完了活兒還是得自己幹,這裡先給大家介紹一個第三方懸浮窗程式碼的具體實現吧,程式碼也比較簡單,可能我一貼出來大家一下就能看懂了。
懸浮窗關鍵程式碼解讀
1.如何申請許可權
首先靜態申請一下
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />複製程式碼
然後為了相容高API,需要進行動態許可權申請,這裡需要根據API版本以及廠商進行區分了。如果版本大於26使用TYPE_APPLICATION_OVERLAY的方式即可,否則使用TYPE_PHONE。其次一般6.0以前是不需要動態申請懸浮窗許可權的,但小米是個特例,而且小米不同版本申請的方式可能還不一樣,在6.0以上申請方式和其他手機一樣,但是6.0以下就不同了
小米6.0以下申請許可權
小米6.0以下申請許可權和小米MIUI版本有關,如下圖,需要對miui版本進行區分。具體的程式碼是我直接用的上面那個第三方庫裡的,有需要的可以去github上拉取直接用。
正常的申請流程,也就是6.0以上手機的申請流程是跳到一個自己封裝的空白許可權申請介面,然後跳轉到系統自帶的懸浮窗申請介面進行許可權申請,不管使用者給不給許可權,都關閉當前空頁面。由於系統不同,所以在不同手機上看到的許可權申請頁面顏值可能就會不一樣。
2.如何實現懸浮窗可隨手指拖動
思路非常簡單,監聽懸浮窗那個imageView的onTouchListener即可,在剛點選的ACTION_DOWN事件中記錄當前的x,y位置,然後在每次移動後獲取到本次移動的位置,二者相減就是需要移動的位置,這是自定義view的最基本操作了。
3.如何實現懸浮窗左右邊的吸頂效果
思路也非常簡單,監聽到手指抬起的動作後,判斷當前位置是靠近左邊還是右邊,靠近左邊就以位置動畫的方式平移到左邊,靠近右邊就平移到右邊。
4.核心,新增懸浮窗的方法
中規中矩,按照android官方的方式新增即可。即獲取系統的WindowManager,設定好引數,呼叫windowManager的addView()方法新增。需要注意的是,在隱藏懸浮窗的時候,最好是移除一下,下次需要顯示的時候再新增。
坑點一覽,滿滿的乾貨
坑點一 多次新增view會導致crash
這個是很多人都會碰到的,有時候沒辦法保證只新增一次,就算給了flag也會因為系統原因導致這個flag不準確。所以我在使用的時候就碰到了多次新增了的情況,最後的結果就是crash,解決方案就是在addView方法上加一個try...catch捕獲住該異常,妥妥的安全感。
坑點二 oppo r9(5.1.1)上是有許可權的,一直給返回沒許可權
這段程式碼是處理6.0以下許可權申請的,上面紅色部分是原來的程式碼,下面綠色部分是改過之後的。由於oppo在6.0以下每次檢測懸浮窗許可權都給false,導致專案中每次都彈框讓使用者去給許可權,最後乾脆就oppo 6.0以下直接預設都有許可權好了。因為事實也如此,除了小米,其他品牌6.0以下都是有許可權的。
坑點三 如何處理輸入法和懸浮窗的層級關係
微信裡的懸浮窗是在輸入法之下的,所以互動的同學也要求我們們的懸浮窗也要在輸入法之下。我檢視了一下WindowManager原始碼,我懸浮窗的優先順序TYPE_APPLICATION_OVERLAY,上面大字寫著明明是在輸入法之下的,但是實際表現是在輸入法之上了。
這個問題也是折騰了半天,在原始碼裡找了半天找得是天昏地暗,終於功夫不負有心人,還真讓我找著了,windowManager是有一個flag專門用來設定懸浮窗和輸入法的關係的,但是由於之前沒設定,所以導致最後預設的輸入法還是在懸浮窗之下。
既然源頭找到了,設定起來就很簡單了,如下圖,設定好flag_alt_focusable_im這個flag即可。後面發現有一個帖子對flag總結得非常好,這裡也推薦給大家,有想了解其他flag的同學可以去看一下這篇帖子:blog.csdn.net/qq_33275597…
坑點四 如何處理退出app或者按home鍵的時候關閉懸浮窗
這也是谷歌坑人的地方,都沒地方設定這個懸浮窗是否只用到app內,所以預設在桌面上也會顯示自己的懸浮窗。比如在微信裡顯示其他app的懸浮窗,這種糟糕的體驗可想而知,使用者不給你解除安裝就真是奇蹟了。為了解決這個問題,最初的實現方式是對所有經過的activity進行記錄,顯示就加1,頁面被掛起就減1,如果減到當前計數為0時說明所有頁面已經關閉了,就可以隱藏懸浮窗了。
但是實際上這麼做還是有問題的,在部分手機上如果是在首頁按返回鍵的話仍然不能隱藏,這個又是系統級的相容性問題。為了解決這問題,我後面又做了一個處理,通過註冊registerActivityLifecycleCallbacks監聽app的前後臺回撥,檢測到如果當前首頁被銷燬時,應該將懸浮窗進行隱藏。
坑點五 多次點選懸浮窗以後,開啟多個頁面
如果你的懸浮窗點選事件是開啟頁面的話,這裡需要注意了,別忘了將這個開啟的頁面的啟動模式設定為singleTop或者是singleTask,從而複用同一個,遠離一直按返回的地獄操作。
android:launchMode="singleTop"複製程式碼
android:launchMode="singleTask"複製程式碼
總結:懸浮窗的坑,只有真正做過的人才懂,不過還好現在已經有很多現成的第三方程式碼可以CV。但是就算是CV了也不代表可以高枕無憂,應用場景不同總還是會遇到其他的問題,我這裡總結了自己遇到的部分坑點,以後還有的話會繼續更新,我相信踩的坑越多,成長才會越快。我也希望自己的工作經歷能夠幫助到大家少走彎路,就像我借鑑了前輩們的成果一樣。
demo地址:github.com/dongrong-fu…