Android使用Window遮罩實現低成本夜間模式
最近要在App中新增夜間模式,一般來說,夜間模式使用主題的方式實現,但因為App中歷史遺留問題較多,更換主題的方式工作量比較大,所以就打算在App的每一個Activity中新增一層半透明的黑色遮罩來實現。
開啟夜間模式時,在每一個Activity的Window中新增一個半透明View,並把View儲存起來,關閉夜間模式時從Window中移除這個View,核心程式碼如下:
// 開啟夜間模式 fun Activity.nightMode(): View { val nightViewParam = WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_APPLICATION, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE /*or WindowManager.LayoutParams.FLAG_FULLSCREEN */ or WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, PixelFormat.TRANSPARENT) nightViewParam.width = ViewGroup.LayoutParams.MATCH_PARENT nightViewParam.height = ViewGroup.LayoutParams.MATCH_PARENT nightViewParam.gravity = Gravity.CENTER val nightView = View(this) nightView.setBackgroundColor(0x99000000.toInt()) windowManager.addView(nightView, nightViewParam) return nightView } // 關閉夜間模式 fun Activity.removeNightMode(view: View?) = view?.let { windowManager.removeViewImmediate(view) }
要在每一個Activity中都去做一次判斷會比較繁瑣,一種方式是通過在基類BaseActivity onStart中新增相關的程式碼判斷當前是否是夜間模式,另一種方式是我在之前的文章Android判斷程式回到前臺並獲取剪貼簿資料
中提到的在Application
中註冊ActivityLifecycleCallbacks的方式來處理。
// Application中 private WeakHashMap<Activity, View> nightModeHashMap = new WeakHashMap<>(); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { checkNightMode(activity); } @Override public void onActivityResumed(Activity activity) { } @Override public void onActivityPaused(Activity activity) { } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { if (nightModeHashMap.containsKey(activity)) { nightModeHashMap.remove(activity); } } }); public void checkNightMode(Activity activity) { if (activity == null) return; if (SharedPreferencesHelper.isNightMode()) { if (!nightModeHashMap.containsKey(activity)) nightModeHashMap.put(activity, ActivityExtensionKt.nightMode(activity)); } else { if (nightModeHashMap.containsKey(activity)) { ActivityExtensionKt.removeNightMode(activity, nightModeHashMap.get(activity)); nightModeHashMap.remove(activity); } } }
這種方式好處是不用修改Activity的程式碼,可以覆蓋App中所有的頁面,甚至是第三方SDK中的頁面,並且後期要移除這個功能的時候也很容易。