当我调用重新创建功能来更改我的应用程序主题时,
SetContent{}
功能不起作用
你好 我尝试更改使用 jetpack compose(material v1.2.0-alpha12)和 android 版本 13(API 30)开发的应用程序中的主题 但提交配置后,活动的
reCreate
功能不起作用
我尝试将 Log.i 放入我的代码中并对其进行调试,我所知道的是在 setContent{}
生命周期之后再次调用时不起作用 reCreate
有人可以帮助我吗?
经过相当长一段时间的修补后,我成功更新了
Locale
并重新创建了更改应用语言的 Activity,同时还使用新的本地化方法在 Android 13 (API 33) 上工作。
我提供了如何实现它的示例指南,包括如何在 Compose 中更改主题。
需要注意的是,我使用Preferences DataStore作为存储首选项的解决方案,而不是Shared Preferences,因为它具有许多优点(例如,无需监听器即可实时观察),并与使用DaggerHilt的依赖注入相结合.
我强烈建议使用具有针对不同屏幕的导航的单个活动,因为它更容易维护,并且应用程序不会被活动填满。这是如何实现此类导航的详细指南。
示例指南的结构:
如果您需要进一步帮助,请随时与我联系。
让我们首先创建一个包含应用程序语言的列表:
val appLanguages = listOf("en", "bg" /* More languages ... */ )
然后,创建一个可以在
Activity
中调用的函数,其中包含设置语言所需的逻辑。我强烈建议将此函数放在一个单独的 Kotlin 文件中,这样它就不会耦合到单个 Activity
,然后以生命周期方式在 onCreate
的 Activity
函数中调用它(如最后一个示例所示) :
fun Activity.setAppLanguage(languageTag: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Change the Locale for Android version >= 13.
val locale = getSystemService(LocaleManager::class.java).applicationLocales
val newLocale = LocaleList(Locale.forLanguageTag(languageTag))
if (locale != newLocale) {
// Set the new Locale.
getSystemService(LocaleManager::class.java).applicationLocales = newLocale
// Recreate the Activity.
recreate()
}
} else {
// Change the Locale for Android version <= 12.
val locale = Locale.getDefault()
val newLocale = Locale(languageTag)
if (locale != newLocale) {
// Apply the new Locale to the configuration.
val configuration = resources.configuration.apply {
Locale.setDefault(newLocale)
setLocale(newLocale)
}
// Update the configuration.
@Suppress("Deprecation")
resources.updateConfiguration(configuration, resources.displayMetrics)
// Recreate the Activity.
recreate()
}
}
}
可以使用以下方法更改 Compose 中的主题:
ThemeMode
枚举类,包含必要的主题模式,例如:SYSTEM
、LIGHT
和DARK
。LocalThemeMode
CompositionLocal 键,用于提供和使用组合中的 ThemeMode
。isInDarkTheme()
可组合函数,用于检查当前应用的主题是否为深色。ThemeMode
。 (如下所示):/** A custom enum class, containing the necessary theme modes of the app. */
enum class ThemeMode {
@RequiresApi(Build.VERSION_CODES.P)
SYSTEM,
LIGHT,
DARK;
companion object {
// SYSTEM theme is available on Android 9+
val Default = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) SYSTEM else LIGHT
}
}
/** CompositionLocal key to provide and consume the applied theme mode down the composition. */
val LocalThemeMode = staticCompositionLocalOf { ThemeMode.Default }
/** A function to check whether the currently applied [ThemeMode] is dark. */
@Composable
fun isInDarkTheme(): Boolean = when (LocalThemeMode.current) {
ThemeMode.DARK -> true
ThemeMode.LIGHT -> false
else -> isSystemInDarkTheme()
}
darkTheme: Boolean
参数替换为 themeMode: ThemeMode
,并使用 LocalThemeMode
提供 CompositionLocalProvider
键,并声明 darkTheme
属性:@Composable
fun MyApplicationTheme(
themeMode: ThemeMode = ThemeMode.Default,
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalThemeMode provides themeMode
) {
val darkTheme = isInDarkTheme()
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
}
创建首选项数据存储:
interface AppPreferences {
// Language
suspend fun setLanguage(language: String)
fun getLanguage(): Flow<String?>
// Theme
suspend fun setTheme(theme: ThemeMode)
fun getTheme(): Flow<ThemeMode?>
}
AppPreferences
的实现类,包含保存和读取首选项的逻辑:class AppPreferencesImpl @Inject constructor(
private val dataStore: DataStore<Preferences>
) : AppPreferences {
companion object {
private val LANGUAGE_KEY = stringPreferencesKey("language")
private val THEME_KEY = stringPreferencesKey("theme")
}
// Language
override suspend fun setLanguage(language: String) {
dataStore.edit { it[LANGUAGE_KEY] = language }
}
override fun getLanguage(): Flow<String?> {
return dataStore.data.map { it[LANGUAGE_KEY] }
}
// Theme
override suspend fun setTheme(theme: ThemeMode) {
dataStore.edit { it[THEME_KEY] = theme.name }
}
override fun getTheme(): Flow<ThemeMode?> {
return dataStore.data.map {
it[THEME_KEY]?.let { theme -> ThemeMode.valueOf(theme) }
}
}
}
设置依赖注入:
@HiltAndroidApp
注释的应用程序类,该类也作为 android:name
属性包含在应用程序清单 application
标记中:@HiltAndroidApp
class App : Application()
<application
android:name=".App">
<!---->
</application>
AppPreferencesModule
,它提供了AppPreferences
的单个实例用于注入:@Module
@InstallIn(SingletonComponent::class)
object AppPreferencesModule {
private val Context.dataStore by preferencesDataStore("appPreferences")
@Provides
@Singleton
fun provideAppPreferences(@ApplicationContext context: Context): AppPreferences {
return AppPreferencesImpl(context.dataStore)
}
}
包含状态和回调的 ViewModel:
ViewModel 有一个
AppPreferences
的注入实例,该实例被转换为状态以供 UI 实时观察:
更改首选项的回调可以在另一个 ViewModel 中实现(例如,设置屏幕,其中也注入了
AppPreferences
实例)。
@HiltViewModel
class MainActivityViewModel @Inject constructor(
private val appPreferences: AppPreferences
) : ViewModel() {
val languageState = appPreferences.getLanguage().filterNotNull().stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000L),
initialValue = Locale.getDefault().toLanguageTag().uppercase()
)
val themeModeState = appPreferences.getTheme().filterNotNull().stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000L),
initialValue = ThemeMode.Default
)
fun setLanguage(languageTag: String) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
appPreferences.setLanguage(languageTag)
}
}
}
fun setTheme(themeMode: ThemeMode) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
appPreferences.setTheme(themeMode)
}
}
}
}
最后是Activity的实现:
该活动用
@AndroidEntryPoint
进行注释,这标志着要设置用于注入的 Android 组件类。
setAppLanguage()
函数以生命周期方式在collected内部执行languageState
。
themeModeState
也以生命周期方式收集,并将其作为参数传递给已经适应的应用程序主题可组合项。
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val viewModel by viewModels<MainActivityViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Set the application language.
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.languageState.collectLatest { setAppLanguage(it) }
}
}
setContent {
val themeMode by viewModel.themeModeState.collectAsStateWithLifecycle()
MyApplicationTheme(
themeMode = themeMode
) {
// ...
}
}
}
}
这就是更新
Locale
并更改 Compose 中主题的示例方法。
如果您需要进一步帮助,请随时与我联系。
快乐编码! 🙌🏼