Jetpack 撰写。强制切换夜间/非夜间资源

Tim*_*lin 5 android android-theme android-resources android-jetpack-compose

最初,我的应用程序的主题遵循系统的主题,为了方便起见,我为浅色和深色主题创建了可绘制资源。然后我实现了手动更改主题的功能,无论系统主题如何,但显然可绘制资源与系统主题或其他主题相关联。有没有办法以某种方式在夜间和非夜间可绘制资源之间强制切换?

Tim*_*lin 7

已找到解决方案,如果有问题请指正。

  1. 从AppCompatActivity继承我们的Activity而不是ComponentActivity

  2. 使用 AppDelegateCompat.setDefaultNightMode() 告诉应用程序我们将使用哪个主题

  3. 调用 delegate.applyDayNight()

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         // Getting user's saved selection
         appTheme.value = runBlocking(Dispatchers.IO) {
             getSavedAppTheme(context)
         }
         // Telling the application which mode to use
         when (appTheme.value) {
             AppTheme.SYSTEM_THEME -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
             AppTheme.NIGHT_THEME  -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
             AppTheme.LIGHT_THEME  -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
         }
         delegate.applyDayNight()
         setContent {
             MyAppTheme {
                 MyAppComposable()
             }
         }
     }
    
    Run Code Online (Sandbox Code Playgroud)

处理用户选择:

    suspend fun setAppTheme(context: Context, theme: AppTheme) {
        // Saving selection to cache 
        saveAppTheme(context, theme)
        // Applying selection
        appTheme.value = theme
        // Telling the application which mode to use
        when (appTheme.value) {
            AppTheme.SYSTEM_THEME -> setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM)
            AppTheme.NIGHT_THEME  -> setDefaultNightMode(MODE_NIGHT_YES)
            AppTheme.LIGHT_THEME  -> setDefaultNightMode(MODE_NIGHT_NO)
        }
    }
Run Code Online (Sandbox Code Playgroud)

更新:

第2点最好在Application.onCreate()中调用,以免在onCreate()内重新加载Activity


Ski*_*ᴉʞS -1

您可以做的是使用数据存储并将该切换的值分配给它。然后在setContent {}您可以将其检查为:

val theme = userSettings.themeStream.collectAsState()
            val useDarkColors = when (theme.value) {
                AppTheme.MODE_AUTO -> isSystemInDarkTheme()
                AppTheme.MODE_DAY -> false
                AppTheme.MODE_NIGHT -> true
            }
Run Code Online (Sandbox Code Playgroud)

其中 userSettings 是:

enum class AppTheme {
    MODE_DAY,
    MODE_NIGHT,
    MODE_AUTO;

    companion object {
        fun fromOrdinal(ordinal: Int) = values()[ordinal]
    }
}

interface UserSettings {
    val themeStream: StateFlow<AppTheme>
    var theme: AppTheme
}

class UserSettingsImpl @Inject constructor(
    @ApplicationContext context: Context
) : UserSettings {

    override val themeStream: MutableStateFlow<AppTheme>
    override var theme: AppTheme by AppThemePreferenceDelegate("app_theme", AppTheme.MODE_AUTO)

    private val preferences: SharedPreferences =
        context.getSharedPreferences("sample_theme", Context.MODE_PRIVATE)

    init {
        themeStream = MutableStateFlow(theme)
    }

    inner class AppThemePreferenceDelegate(
        private val name: String,
        private val default: AppTheme,
    ) : ReadWriteProperty<Any?, AppTheme> {

        override fun getValue(thisRef: Any?, property: KProperty<*>): AppTheme =
            AppTheme.fromOrdinal(preferences.getInt(name, default.ordinal))

        override fun setValue(thisRef: Any?, property: KProperty<*>, value: AppTheme) {
            themeStream.value = value
            preferences.edit {
                putInt(name, value.ordinal)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

那么应该使用存储的Data Store而不是使用系统的。

这是Stefano Sansone的完整示例Compose 主题切换器

您还可以阅读Jetpack Compose Theming