android 深色主题背景适配原理剖析

最。近有个需求,就是APP要适配深色主题,于是乎就查阅相关资料。当然第一手资料就是官网 参考链接 官方资料:深色主题背景官方demo

本文就是在官网资料基础上进行了学习总结,以备不时之需。闲言少数,直接说结论:
1、版本限制:Android 10 (API 级别 29) 及更高版本中提供深色主题背景。

2、在项目res目录下配置如下两个values目录:其中values目录代表正常模式。
values-night代表深色模式使用的颜色值。
在这里插入图片描述
注意,两个values文件夹里的都含有colors.xml文件。里面的color的name都需要一样。另外,values-night/colors.xml文件,其中配置的颜色值在values/colors.xml里面必须都有,否则会造成crash。而且Android Studio会有红色提示,比如我在values-night/colors.xml里配置了test这个颜色。在values/colors.xml没有配置,则会有如下提示:
在这里插入图片描述
但是反过来却不是必须的,values/colors.xml的配置的颜色可以比values-night/colors.xml里面的多。
3、需要注意的是,如果你的View没有使用setBackgroundColor或者TextView没有硬编码设置文本颜色的话,系统会自动根据是否是暗夜模式还是白天模式来匹配对应的颜色。(具体可以看 官方demo)。

4、切换模式的核心方法: AppCompatDelegate.setDefaultNightMode()。,对应代码如下:


public class ThemeHelper {

    public static final String LIGHT_MODE = "light";
    public static final String DARK_MODE = "dark";
    public static final String DEFAULT_MODE = "default";

    public static void applyTheme(@NonNull String themePref) {
        switch (themePref) {
            case LIGHT_MODE: {//普通模式
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
                break;
            }
            case DARK_MODE: {//深色模式
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
                break;
            }
            default: {//跟随系统或者系统默认
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {//Android 10以上系统
                    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
                } else {
                    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);
                }
                break;
            }
        }
    }
}

从上面代码可以看出,因为Android Q及其以上的版本支持深色模式,所以APP如果跟随系统模式的时候,需要做版本判断,对应Android 10及其以上版本,使用: AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); 10以下版本就使用:AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY);

俗话说,知其然,还要知其所以然,那么AppCompatDelegate.setDefaultNightMode都做了什么呢?而AppCompatDelegate在深色模式起到了什么作用呢?下面就来具体分析下,想来看看setDefaultNightMode方法:

    public static void setDefaultNightMode(@NightMode int mode) {
        switch (mode) {
            case MODE_NIGHT_NO://普通模式
            case MODE_NIGHT_YES://页面模式
            case MODE_NIGHT_FOLLOW_SYSTEM://跟随系统
            case MODE_NIGHT_AUTO_TIME://根据时间
            case MODE_NIGHT_AUTO_BATTERY://根据电量
                if (sDefaultNightMode != mode) {//如果模式发生改变
                   //sDefaultNightMode是静态变量
                    sDefaultNightMode = mode;
                    applyDayNightToActiveDelegates();
                }
                break;
        }
    }

从该方法可以看出,深色模式支持多种设定,比如跟随系统模式、根据时间自动调整深色模式、根据电量调整深色模式等等。当模式切换后,需要调用applyDayNightToActiveDelegates,其方法如下

 private static final ArraySet<WeakReference<AppCompatDelegate>> sActivityDelegates =
            new ArraySet<>();
 private static void applyDayNightToActiveDelegates() {
        synchronized (sActivityDelegatesLock) {
            for (WeakReference<AppCompatDelegate> activeDelegate : sActivityDelegates) {
                final AppCompatDelegate delegate = activeDelegate.get();
                if (delegate != null) {
                    delegate.applyDayNight();
                }
            }
        }
    }

AppCompatDelegate类的内部,有一个静态集合sActivityDelegates,用来存储AppCompatDelegate的代理类,比如Activity。当模式发生变化的时候,遍历该集合,然后调用applyDayNight方法实现模式的改变。现在有两个问题需要梳理一下:
1、AppCompatDelegate是什么?
2、什么时候往sActivityDelegates 添加AppCompatDelegate对象?

问题1:AppCompatDelegate是一个抽象类,其设置里面就是一个代理。其中AppCompatDelegateImpl是其之类。
在这里插入图片描述
而且AppCompatDelegate提供了几个create重载方法,用来创建AppCompatDelegate,其中返回的就是AppCompatDelegateImpl。其中是个重载方法分别代理了Activity,Dialog,Context。
在这里插入图片描述
问题2:sActivityDelegates 的集合里的数据从哪儿来。此时AppCompatActivity就需要粉末登场了,来看看。

public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
        TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
            @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
       //获取AppCompatDelegate对象
        final AppCompatDelegate delegate = getDelegate();
        delegate.installViewFactory();
        //调用AppCompatDelegate的onCreate方法
        delegate.onCreate(savedInstanceState);
        super.onCreate(savedInstanceState);
    }
    
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }
}        

可以看出,在AppCompatActivityonCreate方法里调用了getDelegate方法,而getDelegate就是调用了AppCompatDelegate.create返回了一个AppCompatDelegateImpl,并且将AppCompatActivity自身作为参数传了进去。创建好AppCompatDelegateImpl之后,就调用其onCreate方法,就是在onCreate方法里将AppCompatDelegateImpl放进了sActivityDelegates静态集合中去。

    @Override
    public void onCreate(Bundle savedInstanceState) {
        //切换深色或者白天模式,参数传false,表示不重新创建activity
        applyDayNight(false);  

        if (mHost instanceof Activity) {
           //省略部分方法
           //将AppCompatDelegateImpl添加到sActivityDelegates中
            addActiveDelegate(this);
        }

        mCreated = true;
    }

来个小总结:在AppCompatActivity创建的时候,连同AppCompatDelegateImpl一起创建,并将activity传给了AppCompatDelegateImpl,同时将AppCompatDelegateImpl添加到了sActivityDelegates的集合里面,这样在调用当深色模式切换的时候,遍历sActivityDelegates调用applyDayNight进行模式的切换。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付 19.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值