VocabVerse背单词软件功能-主题模式设计

第六周创新实训记录-背单词软件功能主题模式设计

根据之前的软件框架搭建接下来就是相关功能设计以及IDEA中使用git时的问题解决

涉及到的有一下文件

一、themeMode.kt

此文件是一个枚举类型文件,主要用于通过枚举类型,定义了应用支持的多种主题模式,并且每种模式都关联了一个中文描述字符串,便于在界面显示,在应用中,可以直接通过这个枚举来切换以及存储主题模式

enum class ThemeMode(val cnValue: String) {
    LIGHT("浅色"),
    DARK("深色"),
    EYE_CARE("护眼模式"),
    VIBRANT("彩色模式"),
    DEFAULT("按系统设定")
}

二、theme.kt

相关颜色方案的定义以及主题包装函数

//彩色模式下的各个组件的颜色----颜色方案
private val VibrantColors = lightColorScheme(
    primary = Color(0xFF2196F3),  // 亮蓝色
    onPrimary = Color.White,
    background = Color(0xFFE3F2FD),  // 浅蓝色背景
    onBackground = Color.Black,
    surface = Color.White,
    onSurface = Color.Black,
    secondary = Color(0xFFFF9800),  // 橙色
    onSecondary = Color.White
)

主题包装函数

@Composable
fun LandingAppTheme(
    themeMode: ThemeMode = ThemeMode.DEFAULT,  // 使用 ThemeMode 枚举,默认值为ThemeMode.DEFAULT
    content: @Composable () -> Unit
) {
    // 选择颜色方案
    val colors = when (themeMode) {
        ThemeMode.LIGHT -> LightColors
        ThemeMode.DARK -> DarkColors
        ThemeMode.EYE_CARE -> EyeCareColors
        ThemeMode.VIBRANT -> VibrantColors
        ThemeMode.DEFAULT -> if (isSystemInDarkTheme()) DarkColors else DefaultColors
    }

    // 设置系统 UI 颜色
    val systemUiController = rememberSystemUiController() //获取一个系统 UI 控制器
    systemUiController.setSystemBarsColor(
        color = Color.Transparent,  //状态栏颜色设置成透明
        darkIcons = themeMode == ThemeMode.LIGHT || themeMode == ThemeMode.DEFAULT || themeMode == ThemeMode.VIBRANT   //判断是否使用深色图标,浅色、默认、彩色模式下显示深色
    )

    // 应用 MaterialTheme
    MaterialTheme(
        colorScheme = colors,  //将前面的选择颜色方案传入,将整个应用统一
        content = content  //包裹要显示的UI内容,将子组件使用该主题配置
    )
}

三、PreferencesRepository.kt

用于主题模式持久化

首先是有关主题存储键的定义,用于在本地持久化存储中保存和读取用户选择的主题模式

private val themeValue = stringPreferencesKey(THEME_MODE_PREF)

接着是存储主题模式,将用户选择的主题模式存储到 DataStore 中

异步操作--使用 suspend 关键字和 dataStore.edit {} 进行异步存储

数据存储--将枚举值转换为字符串:themeMode.name,并保存到 DataStore 中

异常捕获--捕获 IOException 和其他异常,返回对应的错误类型

suspend fun setTheme(themeMode: ThemeMode): DataResult<String> {
    return try {
        dataStore.edit { pref ->
            pref[themeValue] = themeMode.name
        }
        DataResult.Success("")
    } catch (exception: Exception) {
        if (exception is IOException) {
            DataResult.Error(code = DataResult.Error.Code.IO)
        } else {
            DataResult.Error(code = DataResult.Error.Code.UNKNOWN)
        }
    }
}

最后是读取主题模式,从 DataStore 中读取当前的主题模式,并将其转换为 Flow 流

数据流--dataStore.data用于返回一个流,代表存储中的数据流更新

异常处理--catch {}用于捕获 IOException 异常,并返回 DataResult.Error

数据提取和转换--通过 pref[themeValue] 获取主题值,若不存在,则返回默认主题(ThemeMode.DEFAULT.name),使用 ThemeMode.valueOf(data) 将字符串还原为 ThemeMode 枚举值,最后返回 DataResult.Success(themeMode) 封装结果

fun getThemeValueFlow(): Flow<DataResult<ThemeMode>> {
    return dataStore.data
        .catch {
            DataResult.Error(code = DataResult.Error.Code.IO)
        }
        .map { pref ->
            val data = pref[themeValue] ?: ThemeMode.DEFAULT.name
            val themeMode = ThemeMode.valueOf(data)
            DataResult.Success(themeMode)
        }
}

四、MainViewModel.kt

此文件用于管理应用程序的主界面状态,包括主题设置和用户协议相关逻辑

在实现主题功能时,它通过注入 PreferencesRepository 来访问持久化存储的数据,包括主题模式和用户协议接受状态

首先主题流的获取--调用 PreferencesRepository 中的 getThemeValueFlow() 方法,获取主题模式的数据流(Flow),该流包含用户设置的主题,例如:浅色、深色、护眼模式、彩色模式等

val themeFlow = preferencesRepository.getThemeValueFlow()

使用 combine 将用户协议流和主题流组合在一起,生成联合流

agreementFlow.combine(themeFlow) { agreement, theme ->
    agreement to theme
}

 接着处理主题值

when (themeValue) {
    is DataResult.Error -> {
        mainUiState.value = MainUiState.Error(
            code = themeValue.code
        )  //如果获取主题失败,更新UI状态为Error
    }
    is DataResult.Success -> {
        mainUiState.value = MainUiState.Success(
            startDestination = LandingDestination.Main.Home.route,
            themeMode = themeValue.data
        )  //成功获取主题,更新UI状态为Success,并携带用户设置的主题模式
    }
}

最后时UI状态的更新

mainUiState.value = MainUiState.Success(
    startDestination = LandingDestination.Main.Home.route, //启动页(跳转不同页面)
    themeMode = themeValue.data //用户设置的主题模式
)

更新后的 mainUiState 会自动通知UI 层进行更新,切换主题

五、MainActivity.kt

用于在应用程序启动时正确设置和应用用户选择的主题模式

即根据主题设置动态应用主题,在整个应用程序范围内统一管理主题,即使在加载界面和错误界面,也保持主题一致性,通过这种方法,即使用户在设置中切换主题,整个应用程序的界面都会立刻响应变化,实现实时主题切换

Activity 生命周期设置主题

splashScreen.setKeepOnScreenCondition {
    mainViewModel.uiState.value == MainUiState.Loading  
}

设置内容和主题

动态主题应用--使用 LandingAppTheme(themeMode = uiState.themeMode) 来包裹整个导航图(LandingNavGraphMain),themeMode 通过 ViewModel 获取(即用户选择的主题)

不同主题设置--如果用户选择了深色模式,会传递 isDarkMode = true,否则为 false,将主题与导航图绑定,使整个应用根据主题模式切换

setContent {
    val navHostController = rememberNavController()

    when (val uiState = mainViewModel.uiState.value) {
        is MainUiState.Success -> {
            LandingAppTheme(themeMode = uiState.themeMode) {
                LandingNavGraphMain(
                    isDarkMode = uiState.themeMode == ThemeMode.DARK,
                    playPron = sound::playAudio,
                    navHostController = navHostController,
                    exitApp = this@MainActivity::finish,
                    startDestination = uiState.startDestination
                )
            }
        }
     ...
    }
}

显示加载界面时的主题--通过 LandingAppTheme 包裹,确保加载界面也有统一的主题风格

is MainUiState.Loading -> {
    LandingAppTheme {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier
                .background(MaterialTheme.colorScheme.background)
                .windowInsetsPadding(insets = WindowInsets.systemBars)
                .fillMaxSize()
        ) {
            CircularProgressIndicator(
                modifier = Modifier.size(24.dp, 24.dp)
            )
        }
    }
}

显示错误界面时的主题--通过 LandingAppTheme 包裹,确保错误界面风格统一

is MainUiState.Error -> {
    LandingAppTheme {
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier
                .background(MaterialTheme.colorScheme.background)
                .windowInsetsPadding(insets = WindowInsets.systemBars)
                .fillMaxSize()
        ) {
            ErrorNotice(code = uiState.code)
        }
    }
}

即该文件整体的流程为启动时,显示启动画面,直到 ViewModel 加载完成主题设置;主题加载成功会根据用户设置(或者系统默认),应用正确的主题模式;在应用主题中,使用 LandingAppTheme 包裹整个应用的导航图,确保所有界面都符合主题风格;在界面切换上,即使在错误界面和加载界面,也使用同样的主题风格,保持一致性

六、主题模式功能主要流程

MainActivity 启动并显示启动画面

MainViewModel 初始化并通过 PreferencesRepository 获取主题设置

PreferencesRepository 获取和返回保存的主题模式和协议状态

MainViewModel 根据返回的数据(协议状态、主题模式)更新 UI 状态

MainActivity 根据 UI 状态更新界面,应用主题

LandingAppTheme 负责根据当前主题设置来应用主题模式

七、实现功能图

模式选择

浅色模式

护眼模式

彩色模式

八、IDEA中git使用

首先需要在IDEA中下载git,点开从VCS获取

下面的界面是导入github仓库,当点击下面克隆会IDEA弹出下载git的提示(以及点击下载即可)

其中我当时是从git官网上下载的git,一直使用命令行运行,因为IDEA自动下载的git和之前版本不一致,因此它刚开始提示我下载错误,之后又提示我已经存在git

解决方案--打开设置->版本控制->git

上面的可执行文件选择之前下载的git.exe文件,成功导入

之后可以直接在IDEA里面实现相关git操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值