暗色模式实现思路

先介绍应用的项目框架场景。

我们的项目框架是 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);
}

以上就是全部实现方法;

使用:

  1. 实现DarkmListener 接口  implements DarkModeListener
  2. 添加观察者模式  TeamMessageCenter.getInstance().addDarkModeListener(this);实现onNightModeChanged(boolean isDarkMode) 方法
  3. 设置模式 DarkModeUtils.applyDayMode();

以下是这么实现的原因:

官方实现方式其实很简单,设置暗夜模式后,只要让界面重建recreat()之后

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);

系统就会自动寻找values-night 文件下对应色素了。但是因为我使用的Activity+NFragment方式不想让Activity去进行重建。并且重建的话 会出现闪屏。

所以就应用了文章开头所说的 比较笨拙的方法

记录Compose编写实现暗夜模式:

  1. 创建自定义主题颜色
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
    )
}
  1. 编码实现切换主题颜色
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 = {})
        }
    }
}

上手试了一下,是真的香。哎,只能说拥抱新的技术把,不然真的会快速被淘汰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值