haohao

Android 主题切换和多主题实现

Cover

又是一个周末

白天和夜间模式

Android官方在Support包从23.2版本开始提供支持白天夜间模式的主题 Theme.AppCompat.DayNight,也就是 DayNight Mode ,轻松实现主体无缝切换。

Theme.AppCompat.DayNight 可以根据系统时间切换 Theme.AppCompat (暗色) 和 Theme.AppCompat.Light ( 亮色 ) 两种主题。这将大大提高了阅读类应用的用户体验 同时还支持 Material Design。

需要注意的是,这个特性只支持 API v14 及以上的 Android 设备,在 API v14 以下的设备则会默认使用亮色的主题。

效果预览

效果预览

具体过程

1. 引入 support-v7:23.2+ 版本的依赖

1
compile 'com.android.support:appcompat-v7:25.1.1'

为保证主题风格的统一,所有的 Activity 类应继承 AppCompatActivity 。

2. 自定义实现 DayNight 主题

res-night

res 目录下新建 values-night 文件,拷贝修改 colors.xml 和 styles.xml 文件到 values-night,为 Night Model 下系统加载的 color资源,同样地也可以指定 Night Model 下加载的 drawable 资源在 drawable-night 文件夹中,同理 layout mipmap 资源加载也是如此。

1
2
3
4
5
6
<style name="MyApp.DayNight.NoActionBar.Theme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColorPrimary">@color/colorAccent</item>
</style>

具体位置颜色设置看图:

screen

在 AndroidManifest.xml 中声明,单个 Activity 的主题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/MyApp.DayNight.NoActionBar.Theme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

或者整个 App 的主题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/MyApp.DayNight.NoActionBar.Theme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

3 设置 DayNight 主题

初始化主题:

1
2
3
4
5
6
7
8
9
10
11
private void initAppTheme() {
if (getThemeSharedPref().getBoolean(KEY_NIGHT_THEME, false)) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
}
private SharedPreferences getThemeSharedPref() {
return getSharedPreferences("APP_THEME", Context.MODE_PRIVATE);
}

初始化主题应该在 super.onCreate() 之前执行,不然 Activity 会启动 2 次,下图为证:

restart

切换主题:

1
2
3
4
5
6
7
if (id == R.id.set_day_theme) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_NO);
getThemeSharedPref().edit().putBoolean(KEY_NIGHT_THEME,false).apply();
} else if (id == R.id.set_night_theme) {
getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);
getThemeSharedPref().edit().putBoolean(KEY_NIGHT_THEME, true).apply();
}

切换主题会重启 Activity。

DayNight Theme 有 4 种 Model ,分别是:

  • MODE_NIGHT_NO. 使用 Day 主题
  • MODE_NIGHT_YES. 使用暗色 Night 主题
  • MODE_NIGHT_AUTO. 根据系统时间自动切换
  • MODE_NIGHT_FOLLOW_SYSTEM(默认选项). 设置为跟随系统

多主题实现

介绍几种换肤的第三方库。

MultipleTheme

真正的支持无缝换肤/夜间模式的Android框架,配合theme和换肤控件框架可以做到无缝切换换肤(无需重启应用和当前页面)。

该应用框架可以实现无缝换肤/切换夜间模式的需求,需要在换肤/切换夜间模式的界面只需要使用框架里的自封装控件,其他界面的控件使用原生android控件即可。

缺陷:各种控件都需要重定义。
优势:扩展性强,无需重启 Activity。

Colorful [推荐]

Colorful is a dynamic theme library allowing you to change your apps’ color schemes easily.

缺陷:主题颜色可定制性差,不支持 Material Design 风格。
优势:使用方便。

Prism

Prism

Prism 下含有三个库:

  • prsim 库含有一些核心功能;
  • prism-viewpager 库实现了核心库与 ViewPager 的对接;
  • prism-palette 库实现了核心库与 Palette 的对接。

分成三个库是为了区分依赖条件:核心库不依赖外部条件,它能够很容易地添加到你的工程之中;但是 prism-viewpager 和 prism-palette 需要依赖相应的support库。所以当你的程序不使用这些依赖库时,你可以只使用 prism 库来省去不必要的依赖条件。不过当你的程序中使用了 ViewPager 时,即已经对相关的support库有了依赖,那么添加 prism-viewpager 库就不需要额外的依赖条件。

但是,作者已停止更新。

Android-Skin-Loader [推荐]

看效果

skin-loader

项目目录:

项目目录

Android-Skin-Loader
├── android-skin-loader-lib // 皮肤加载库
├── android-skin-loader-sample // 皮肤库应用实例
├── android-skin-loader-skin // 皮肤包生成demo
└── skin-package // 皮肤包输出目录

可以将皮肤文件打包分离,支持在线换肤,皮肤共享,不会重启 Activity 或 Fragment 。皮肤包( 后缀名为.skin )的本质是一个apk文件,该apk文件不包含代码,只包含资源文件。

缺陷:每个换肤的 View 都要设置 skin:enable="true" 繁琐 ( 反正我感觉挺烦的 ),不支持 Material Design 风格。

不过,还好 ThemeSkinning 进行了优化,支持 Material Design 和字体的切换。

示例:

sample




联系我

Wechat ID

公众号

生活不止于眼前的苟且, 还有诗和远方的田野