日夜间及换肤(一)-常用技巧
本文于 1232 天之前发表,文中内容可能已经过时。
[TOC]
原地址:日夜间及换肤(一)-常用技巧
总览
实现日夜间的方式有多种,基本可以整理如下:
- 设置UiMode来设置
- 对Activity设置主题来变换
- 动态设置资源,控制view刷新
UIMode实现
在value的同级目录下新建一个
values-night
的目录,其中新建colors.xml文件,themes.xml文件等需要日夜间切换比如colors文件,需要将color所对应的名字保持一致,只进行颜色上的更改,达到日夜间不同的效果
1
2
3
4
5
6
7//normal
<color name="background">#FFFFFFFF</color> //白色
<color name="text_color">#FF000000</color> //黑色
//night
<color name="background">#FF000000</color> //黑色
<color name="text_color">#FFFFFFFF</color> //白色在需要触发切换的地方调用如下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33//设置所有的Activity生效
if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES) {
//切换白天
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
} else {
//切换夜间
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
//设置所在的Activity生效
if(delegate.localNightMode == AppCompatDelegate.MODE_NIGHT_YES){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
//切换白天
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_NO
}
}else{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
//切换夜间
delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES
}
}
//五种参数类型
//跟随系统
MODE_NIGHT_FOLLOW_SYSTEM
//跟随时间自动设置
MODE_NIGHT_AUTO_TIME
//日间
MODE_NIGHT_NO
//夜间
MODE_NIGHT_YES
//根据系统电量决定暗色和亮色展示
MODE_NIGHT_AUTO_BATTERY
系统会根据日夜间自动去取*-night
或者正常的文件夹相对应的配置,配合持久化存储进行日夜间状态存储,二次打开加载上一次设置的模式
设置Activity主题实现
在colors.xml中新建日夜间两种颜色
1
2
3
4
5<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="background_day">#FFFFFFFF</color>
<color name="background_night">#FF000000</color>
</resources>在res/values目录下新建attrs.xml文件,添加自定义属性
1
2
3
4<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="custom_bg" format="color|reference"/>
</resources>在res/values目录下新建themes.xml文件,添加两种主题如下:
1
2
3
4
5
6
7
8
9<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.MyDay" parent="Theme.AppCompat">
<item name="custom_bg"> /background_day</item>
</style>
<style name="Theme.MyNight" parent="Theme.AppCompat">
<item name="custom_bg"> /background_night</item>
</style>
</resources>在布局文件中使用自定义属性颜色
1
2
3
4
5
6
7<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/custom_bg"
tools:context=".MainActivity">
...
</androidx.core.widget.NestedScrollView>在Activity中配置
只在当前Activity中生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38class MainActivity : AppCompatActivity() {
private var myTheme = R.style.Theme_MyDay
private lateinit var binding:ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 判断是否有主题存储
if(savedInstanceState != null){
myTheme = savedInstanceState.getInt("theme")
setTheme(myTheme)
}
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initView()
}
private fun initView() {
binding.button2.setOnClickListener {
myTheme = if(myTheme == R.style.Theme_MyDay){
R.style.Theme_MyNight
}else{
R.style.Theme_MyDay
}
recreate()
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt("theme", myTheme)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
myTheme = savedInstanceState.getInt("theme")
}
}因为setContentView()时进行主题加载和布局渲染,所以需要在setContentView()之前调用
setTheme
方法,如需动态替换日夜间模式,同样需要recreate()
才能生效,因为牵扯到Activity的重建,所以我们第一反应是通过onSaveInstance存储主题,在onCreate中进行加载,当然也可以使用其他方式。在所有Activity生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35class App : Application() {
companion object{
var myTheme = R.style.Theme_MyDay
}
var activityLifecycle = object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
activity.setTheme(myTheme)
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
}
}
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(activityLifecycle)
}
}在Application中进行设置,用静态对象存储主题,监听应用下所有Activity的创建,在
onActivityCreate()
时,进行主题设置,这样对Activity来说是无感的,只需要设置主题即可。当然也可以做一个持久化存储,这里只是提供一个思路
动态设置资源,控制view刷新
比较代表性的就是Android-skin-support这个库
添加如下依赖:
1 | implementation 'skin.support:skin-support:4.0.5' // skin-support |
在Application的onCreate中进行初始化操作
1 |
|
如果项目中使用的Activity继承自AppCompatActivity,需要重载getDelegate()方法
1 |
|
通过如下方法触发
1 | SkinCompatManager.getInstance().loadSkin("night", SkinCompatManager.SKIN_LOADER_STRATEGY_BUILD_IN); // 后缀加载 |
详细用法查看README
对比
- UIMode和theme替换都需要重建Activity,所以会导致闪屏,动态替换会更加流畅
- recreate()是在API 11添加进来的,所以会在Android 2.x中使用抛出异常
- theme换肤起来会更加方便