Android 無障礙全域性懸浮窗實現方案
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第6天,點選檢視活動詳情
Android 無障礙的全域性懸浮窗可以在螢幕上新增 UI 供使用者進行快捷操作,可以展示在所有應用程式之上長期展示。另一方面,在一些自動化場景下,可以用來遮蔽使用者行為,防止使用者手動操作打斷自動化流程。
無障礙新增 UI
無障礙服務新增 UI 十分簡單,使用 LayoutInflater 在 AccessibilityService 的 onServiceConnected
新增一個 UI:
```kotlin // in AccessibilityService, service 代表 AccessibilityService 的子類例項 private fun initView() { // 在螢幕頂部新增一個 View val wm = service.getSystemService(AccessibilityService.WINDOW_SERVICE) as? WindowManager val lp = WindowManager.LayoutParams().apply { type = TYPE_ACCESSIBILITY_OVERLAY // 因為此許可權才能展示處理 layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES format = PixelFormat.TRANSLUCENT flags = flags or FLAG_LAYOUT_NO_LIMITS or FLAG_NOT_TOUCHABLE or // 透傳觸控事件 FLAG_NOT_FOCUSABLE or // 透傳輸入事件 FLAG_LAYOUT_IN_SCREEN width = MATCH_PARENT height = MATCH_PARENT } // 通過 LayoutInflater 建立 View val rootView = LayoutInflater.from(service).inflate(R.layout.float_layer, null) wm?.addView(rootView, lp) }
```
然後在自定義的無障礙服務中去呼叫這個方法:
kotlin
class MyAccessibilityService: AccessibilityService() {
override fun onServiceConnected() {
super.onServiceConnected()
initView()
}
// ...
}
需要注意的是,這裡不能將 initView
新增到 onCreate
生命週期中,官方文件也有一些放在 onCreate 中的操作,但實際上都會導致 crash 。
log
java.lang.RuntimeException: Unable to create service com.chunyu.accessibilitydemo.service.AccessibilityDemoService: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
無障礙服務所有的初始化工作,都要放在 onServiceConnected
中執行。這樣就可以將自定義的 UI 展示到螢幕上了。
關於無障礙服務的配置,可以參考官方 API 。
配置分析
從使用上來看,無障礙蒙層是通過 WindowManager 新增到螢幕上的。而關鍵的一些資訊在 WindowManager.LayoutParams 配置的資料中。
Type
Window 有一個關鍵的屬性 type ,它被定義在 WindowManager 的內部類 LayoutParams 中,它可以控制 Window 的顯示次序。主要分為三種:
- Application Window:應用程式視窗 1-99 ,應用程式視窗一般位於最底層。
- System Window:系統視窗 2000-2999 ,系統級視窗一般位於最頂層,不會被其他的window遮住。
- Sub Window:子視窗 1000-1999,子視窗一般是顯示在應用視窗之上。
從三種視窗的值也可推斷出,type 的值越大,Window 就越靠近使用者。
在上面的使用中,我們將 type 設定為 TYPE_ACCESSIBILITY_OVERLAY
,它的值是 2032 ,是一個系統視窗,所以可以展示在應用程式之上。 TYPE_ACCESSIBILITY_OVERLAY
,是無障礙服務用來展示 UI 專用的 視窗型別 。使用它可以在所有的應用程式上展示蒙層。
Flag
flag 中包含了兩個關鍵的值 FLAG_NOT_TOUCHABLE
和 FLAG_NOT_FOCUSABLE
,和一些其他的 flag 。配置這兩個內容,蒙層將不會影響任何使用者操作。
-
FLAG_NOT_TOUCHABLE
:可以將 Window 設定為永不接收觸控事件,從而能夠將觸控事件透傳給蒙層遮蓋住的區域,不阻塞使用者操作。 -
FLAG_NOT_FOCUSABLE
:可以將 Window 設定為永不獲取按鍵輸入焦點,使用者無法向這個 Window 傳送按鍵或其他的按鈕時間,而被它覆蓋的內容可以接收並響應事件。 FLAG_LAYOUT_NO_LIMITS
:允許視窗延伸到螢幕之外。FLAG_LAYOUT_IN_SCREEN
:將視窗放置在整個螢幕中,忽略來自父視窗的任何約束。
LayoutInDisplayCutoutMode
這個屬性可以用來控制 Window 在劉海屏的佈局方式。
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
:僅當劉海屏完全包含在系統欄中時,才允許視窗擴充套件到劉海區域。 否則,視窗的佈局使其不與劉海區域重疊。LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
:允許 Window 延伸到短的一側邊緣的劉海區域。LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
:Window 不允許延伸到劉海屏區域。LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
:允許 Window 延伸到所有的螢幕邊緣劉海區域。
- 通知監控 NotificationListenerService 的 onNotificationPosted 重複回撥問題
- Android View 知識體系
- Kotlin 協程的取消機制超詳細解讀
- Android ViewPager2 使用 自定義指示器檢視
- Android ViewModel 超詳細分析
- Android 無障礙監聽通知的過程
- ADB 模擬輸入事件總結
- Android 單元測試基礎
- Java 多執行緒併發【13】FutureTask
- Android UI 測試基礎
- Android 無障礙全域性懸浮窗實現方案
- Java 多執行緒併發 【11】ReentrantReadWriteLock
- Java 多執行緒併發 【10】ReentrantLock
- Java 多執行緒併發【8】LockSupport
- Java 多執行緒併發【4】虛擬機器鎖優化方案
- 散列表 及其在 JDK 中的實現
- Android 應用架構指南
- Kotlin/Java 資料型別的底層邏輯
- Android 主執行緒一定是 UI 執行緒嗎?
- Context.getSystemService 獲取 Manager 的底層實現