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中的页面,并且后期要移除这个功能的时候也很容易。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 173,466评论 25 708
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,970评论 2 59
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明AGI阅读 16,017评论 3 119
  • 紧靠着马路的边缘走 避过车流,显眼的红绿灯 霓虹烁烁的亮光,隐形在夜晚 划分出一条相对安全的斑马线 紧挨着一排树眺...
    鹿右右阅读 292评论 0 8
  • SDWebImage底层实现有沙盒缓存机制,主要由三块组成 1、内存图片缓存 2、内存操作缓存 3、磁盘沙盒缓存 ...
    Coder_JMicheal阅读 285评论 0 1