为什么更改设置时主题不改变,而是重新进入应用程序时才改变?喷气背包组成

问题描述 投票:0回答:1

有一个 mainActivity,其中调用 navHost 并在其中调用抽屉,并从中调用设置屏幕

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        WindowCompat.setDecorFitsSystemWindows(window, false)
        super.onCreate(savedInstanceState)
        setContent {
            val viewModel: SettingsViewModel = hiltViewModel()
            val activityState by viewModel.appTheme.collectAsStateWithLifecycle()
            LaunchedEffect(Unit) {
                viewModel.getTheme(this@MainActivity)
            }

            AppTheme(
                isDarkTheme = shouldUseDarkTheme(appTheme = activityState)
            ) {
                TopNavHost()
            }
        }
    }
}

用于设置的viewModel:

class SettingsViewModel: ViewModel() {

    private val _appTheme = MutableStateFlow(AppTheme.System)
    val appTheme: StateFlow<AppTheme> = _appTheme

    fun getTheme(context: Context) {
        viewModelScope.launch {
            _appTheme.value = withContext(Dispatchers.IO) {
                DataStoreManager(context).getTheme()
            }
        }
    }

    fun saveTheme(context: Context, newTheme: AppTheme) {
        _appTheme.value = newTheme
        viewModelScope.launch {
            DataStoreManager(context).saveTheme(_appTheme.value.name)
        }
    }
}

以及设置屏幕本身

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(
    onBack: () -> Unit,
    viewModel: SettingsViewModel = hiltViewModel()
) {
    val context = LocalContext.current

    LaunchedEffect(Unit) {
        viewModel.getTheme(context)
    }
    Scaffold(topBar = {
        TopAppBar(
            title = {
                Text(
                    text = stringResource(id = R.string.settings),
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis
                )
            },
            navigationIcon = {
                IconButton(onClick = { onBack() }) {
                    Icon(
                        imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                        contentDescription = "back"
                    )
                }
            }
        )
    },
        content = { innerPadding ->
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(innerPadding)
                    .padding(horizontal = 10.dp)
            ) {
                ThemeSettings(viewModel.appTheme.collectAsStateWithLifecycle().value) {
                    viewModel.saveTheme(context, it)
                }
                HorizontalDivider(
                    modifier = Modifier.padding(top = 5.dp, bottom = 10.dp),
                    thickness = 1.dp
                )
                LanguageSettings()
                HorizontalDivider(
                    modifier = Modifier.padding(top = 5.dp, bottom = 10.dp),
                    thickness = 1.dp
                )
            }
        })
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun ThemeSettings(appTheme: AppTheme, onAppThemeChanged: (AppTheme) -> Unit) {
    val themes = arrayOf(
        stringResource(id = R.string.system_theme),
        stringResource(id = R.string.dark_theme),
        stringResource(id = R.string.light_theme)
    )

    Column(
        modifier = Modifier.fillMaxWidth()
    ) {
        Spacer(modifier = Modifier.height(5.dp))
        Text(text = stringResource(id = R.string.theme))
        FlowRow(
            modifier = Modifier.fillMaxWidth()
        ) {
            TextedRadioButton(
                selected = appTheme == AppTheme.System,
                onClick = {
                    if (appTheme != AppTheme.System)
                        onAppThemeChanged(AppTheme.System)
                },
                text = themes[0]
            )
            TextedRadioButton(
                selected = appTheme == AppTheme.Light,
                onClick = {
                    if (appTheme != AppTheme.Light)
                        onAppThemeChanged(AppTheme.Light)

                },
                text = themes[2]
            )
            TextedRadioButton(
                selected = appTheme == AppTheme.Dark,
                onClick = {
                    if (appTheme != AppTheme.Dark)
                        onAppThemeChanged(AppTheme.Dark)
                },
                text = themes[1]
            )

        }
    }
}

如果我直接从 MainActivity 调用 settingsScreen,则主题会更改而无需重新进入应用程序。 我知道我的 MainActivity 仅在应用程序打开时启动,但我不明白如何直接从设置屏幕更改主题。

这是主题代码:

@Composable
fun AppTheme(
    isDarkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {

    val colorScheme = if (isDarkTheme) DarkColors else LightColors
    if (Build.VERSION.SDK_INT >= 29) {
        LocalView.current.isForceDarkAllowed = false
    }
    val systemUiController = rememberSystemUiController()
    SideEffect {
        systemUiController.setStatusBarColor(
            color = Color.Transparent,
            darkIcons = !isDarkTheme
        )
        systemUiController.setNavigationBarColor(
            color = Color.Transparent,
            darkIcons = !isDarkTheme
        )
    }

    MaterialTheme(
        colorScheme = colorScheme,
        content = {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = colorScheme.background,
                contentColor = colorScheme.onBackground,
                content = content
            )
        }
    )

}
android kotlin android-jetpack-compose android-theme
1个回答
0
投票

根据您提供的代码,看起来有两个原因:

1。您正在使用 settingsViewModel 的两个实例

在您的代码中,仅针对

_appTheme.value = newTheme
上的
SettingsViewModel
实例调用
SettingsScreen
,另一个(在
MainActivity
内)保持不变。

这没有什么问题,但您的

DataStoreManager
应该提供如何在多个屏幕上共享单个
AppTheme
的方法。

2。您没有观察到您的

AppTheme
的变化。

在您的代码中,在

AppTheme
中获得
LaunchedEffect
,这是单次操作(在您的代码中,而不是一般情况下)。这就是为什么只有当您再次进入应用程序时它才起作用。

解决方案

我不知道你的

DataStoreManager
,所以我创建了这个。使用
hilt
@Singleton
确保您只有一个实例。

@Singleton
class DataStoreManager @Inject constructor(
   @ApplicationContext
    private val context: Context
) {

    val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "test data store")
    val key = stringPreferencesKey("appTheme")
    //Flow that gets updated every time you update theme
    val themeFlow = context.dataStore.data.map { AppTheme.byName(it[key] ?: "") }

    suspend fun saveTheme(name: String) {
        context.dataStore.edit {
            it[key] = name
        }
    }
}

通过此功能,您可以收集

AppTheme
中的
SettingsViewModel
更改并进一步发送。现在不需要
getTheme
功能,主题正在更新中。

@HiltViewModel
class SettingsViewModel @Inject constructor(
    context: Application,
    val dataStore: DataStoreManager
) : ViewModel() {

    private val _appTheme = MutableStateFlow(AppTheme.System)
    val appTheme: StateFlow<AppTheme> = _appTheme

    init {
        viewModelScope.launch {
            //Collecting theme
            dataStore.themeFlow.collect { newTheme ->
                _appTheme.value = newTheme
            }
        }
    }

    fun saveTheme(newTheme: AppTheme) {
        //Now this is not necessary because once you save theme
        //_appTheme gets updated 
        //_appTheme.value = newTheme
        viewModelScope.launch {
            dataStore.saveTheme(newTheme.name)
        }
    }
}

这样,您的主题更改将立即可见。

© www.soinside.com 2019 - 2024. All rights reserved.