先介绍应用的项目框架场景。
我们的项目框架是 Mvvm + 组件化。 因为是地图项目,考虑地图因素,采用 Activity + NFragment的形式。 历史原因不赘述,(组件化模式并不适合 Activity + NFragment的形式开发)
实现暗色模式的方案;主要记录系统实现方式,不
- 系统实现方式 values-night
- 三方库换肤方案 (不赘述)
- 其他实现方式
- 补充 Compose 实现暗夜模式是真的香
记录采用系统实现方式,但是我不想让Activity走重建,所以采用的笨拙方式;
1、创建广播,监听系统
public class DarkModeBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (TeamMessageCenter.getInstance().getDarkMode() ==-1){
boolean isDarkMode = DarkModeUtils.isDarkMode(context);
TeamMessageCenter.getInstance().notifyDarkModeChanged(isDarkMode);
}
}
}
除了广播的形式,也是重写 onConfigurationChanged() 方法
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
两种方式二选一,主要应对设置跟随系统模式后,系统自动变换暗色模式的场景。
2、DarkModeUtils 工具类
public class DarkModeUtils {
/**
* 应用夜间模式
*/
public static void applyNightMode() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
MkProvider.getInstance().put(C.DARK_MODE,AppCompatDelegate.MODE_NIGHT_YES);
TeamMessageCenter.getInstance().notifyDarkModeChanged(true);
}
/**
* 应用日间模式
*/
public static void applyDayMode() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
MkProvider.getInstance().put(C.DARK_MODE,AppCompatDelegate.MODE_NIGHT_NO);
TeamMessageCenter.getInstance().notifyDarkModeChanged(false);
}
/**
* 跟随系统主题时需要动态切换
*/
public static void applySystemMode() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
MkProvider.getInstance().put(C.DARK_MODE,AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
TeamMessageCenter.getInstance().notifyDarkModeChanged(DarkModeUtils.isDarkMode(AppProvider.getInstance().getApp()));
}
/**
* 判断App当前是否处于暗黑模式状态
*/
public static boolean isDarkMode(Context context) {
int nightMode = MkProvider.getInstance().getInt(C.DARK_MODE);
if (nightMode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
int applicationUiMode = context.getResources().getConfiguration().uiMode;
int systemMode = applicationUiMode & Configuration.UI_MODE_NIGHT_MASK;
return systemMode == Configuration.UI_MODE_NIGHT_YES;
} else {
return nightMode == AppCompatDelegate.MODE_NIGHT_YES;
}
}
}
其中 MkProvider.getInstance().getInt(C.DARK_MODE); 是使用sp方式记录当前模式;
3、TeamMessageCenter 参考 消息总站 是利用接口做的一个回调通知
public class TeamMessageCenter {
private static TeamMessageCenter instance;
private final List<DarkModeListener> darkModeListeners = new ArrayList<>(); //
private TeamMessageCenter() {
}
public static synchronized TeamMessageCenter getInstance() {
if (instance == null) {
instance = new TeamMessageCenter();
}
return instance;
}
/**
* 添加暗夜模式监听器
*/
public void addDarkModeListener(DarkModeListener listener) {
darkModeListeners.add(listener);
}
/**
* 移除暗夜模式监听器
*/
public void removeDarkModeListener(DarkModeListener listener) {
darkModeListeners.remove(listener);
}
/**
* 通知所有暗夜模式监听器
*/
public void notifyDarkModeChanged(boolean isDarkMode) {
for (DarkModeListener listener : darkModeListeners) {
listener.onNightModeChanged(isDarkMode);
}
}
}
4、DarkModeListener 接口
public interface DarkModeListener {
void onNightModeChanged(boolean isDarkMode);
}
以上就是全部实现方法;
使用:
- 实现DarkmListener 接口 implements DarkModeListener
- 添加观察者模式 TeamMessageCenter.getInstance().addDarkModeListener(this);实现onNightModeChanged(boolean isDarkMode) 方法
- 设置模式 DarkModeUtils.applyDayMode();
以下是这么实现的原因:
官方实现方式其实很简单,设置暗夜模式后,只要让界面重建recreat()之后
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
系统就会自动寻找values-night 文件下对应色素了。但是因为我使用的Activity+NFragment方式不想让Activity去进行重建。并且重建的话 会出现闪屏。
所以就应用了文章开头所说的 比较笨拙的方法
记录Compose编写实现暗夜模式:
- 创建自定义主题颜色
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFFBB86FC),
secondary = Color(0xFF03DAC6),
background = Color(0xFF121212),
surface = Color(0xFF121212),
onPrimary = Color.Black,
onSecondary = Color.Black,
onBackground = Color.White,
onSurface = Color.White,
)
private val LightColorScheme = lightColorScheme(
primary = Color(0xFF6200EE),
secondary = Color(0xFF03DAC6),
background = Color(0xFFFFFFFF),
surface = Color(0xFFFFFFFF),
onPrimary = Color.White,
onSecondary = Color.Black,
onBackground = Color.Black,
onSurface = Color.Black,
)
@Composable
fun ThemeChangeTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
- 编码实现切换主题颜色
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var isDarkTheme by remember { mutableStateOf(false) }
ThemeChangeTheme(darkTheme = isDarkTheme) {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ThemeSwitcher(isDarkTheme) { isDarkTheme = it }
}
}
}
}
@Composable
fun ThemeSwitcher(isDarkTheme: Boolean, onThemeChange: (Boolean) -> Unit) {
val context = LocalContext.current
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = if (isDarkTheme) "当前是暗夜模式" else "当前是白天模式",
color = if (isDarkTheme) Color.White else Color.Black
)
Spacer(modifier = Modifier.height(20.dp))
Row {
Button(onClick = {
onThemeChange(false)
Toast.makeText(context, "切换到白天模式", Toast.LENGTH_SHORT).show()
}) {
Text("白天模式")
}
Spacer(modifier = Modifier.width(8.dp))
Button(onClick = {
onThemeChange(true)
Toast.makeText(context, "切换到暗夜模式", Toast.LENGTH_SHORT).show()
}) {
Text("暗夜模式")
}
}
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
ThemeChangeTheme {
ThemeSwitcher(isDarkTheme = false, onThemeChange = {})
}
}
}
上手试了一下,是真的香。哎,只能说拥抱新的技术把,不然真的会快速被淘汰。