Vocabverse单词学习功能开发--第七周

上周对于数据库的设计、搭建与优化已基本完成,对于基础功能也进行了一定的开发,本周

主要是对于单词列表、单词学习、单选练习、单词默写练习等功能的开发。


本周开发的所有功能均在study文件夹中,架构如下所示:

 

一、单词列表显示功能的开发

1.根据学习计划的选择和设定,WordListViewModel分别接受三个参数:start、wordListsize和vocabularyName,根据这三个参数从数据库中取出相应的单词,代码实现如下:

 val progress = studyProgressRepository.getStudyProgressLatest()
            if (progress != null) {
                val wordList = vocabularyViewRepository.getWordList(
                    start = progress.start,
                    wordListSize = progress.wordListSize,
                    vocabularyName = progress.vocabularyName
                )
                wordListUiState.value = WordListUiState
                    .Success(
                        wordList = wordList
                    )
            } else {
                wordListUiState.value = WordListUiState
                    .Error(DataResult.Error.Code.UNKNOWN)
            }

2.在此功能的实现过程中,设置了缓存机制,其实现逻辑如下:

sequenceDiagram
    ViewModel->>Repository: getWordList(start=0, size=10, BEGINNER)
    alt 缓存命中
        Repository-->>ViewModel: 返回缓存数据
    else 缓存未命中
        Repository->>DAO: 查询数据库
        DAO-->>Repository: 返回数据
        Repository->>Repository: 更新缓存
        Repository-->>ViewModel: 返回数据
    end

 此处的缓存机制通过一个列表实现对于数据的暂时存储,命中与清空更新缓存代码如下:

//命中
suspend fun getWordList(
        start: Int,
        wordListSize: Int,
        vocabularyName: Vocabulary.Name
    ): List<Word> {
        if (beginCache != start || numCache != wordListSize) {
            beginCache = start
            numCache = wordListSize
            wordListCache = when (vocabularyName) {
                Vocabulary.Name.BEGINNER -> getWordListBeginner(start, wordListSize)
                Vocabulary.Name.INTERMEDIATE -> getWordListIntermediate(start, wordListSize)
                Vocabulary.Name.NONE -> emptyList()
            }
        }
        return wordListCache
    }

//清空缓存
fun emptyWordListCache() {
        if (wordListCache.isNotEmpty()) {
            wordListCache = emptyList()
            beginCache = -1
            numCache = -1
        }
    }

最终实现页面如下所示:

二、单词学习功能的开发

1.功能架构:在单词学习功能的开发过程中,有一个创新点--实现横向翻页切换功能。具体实现:通过LearnScreen对横向的两个页面进行管理,0->>LearnPager,1 ->> QuizPager。LearnPager和QuizPager两个kt文件分别用于管理两个页面的组件和内容。架构逻辑图如下:

A[LearnScreen] --> B[LearnContent]
    B --> C[状态管理: LearnUiState]
    B --> D[横向分页: LearnPager + QuizPager]
    B --> E[底部操作栏: InputButtonRow]

2.创新点设计:

分页同步控制:触发时机:点击"下一题"时自动返回学习页(第0页)

scope.launch {
    pagerState.animateScrollToPage(0)  // 切换到学习页
}

状态驱动UI:根据是否最后一题、是否答对动态改变按钮功能和图标

val lastOne = (uiState.current + 1) == uiState.totalNum
val rightActionInfo = if (uiState.learned && lastOne) {
    R.drawable.ic_start_24dp to { navigateToChoice() }  // 完成学习
} else {
    R.drawable.ic_double_arrow_right_24dp to getNextWord  // 下一题
}

协程动画控制:平滑处理分页切换,统一视觉风格,并通过底部操作栏设计便于用户使用。

InputButtonRow(
    leftButtonIconId = R.drawable.ic_submit_24dp,  // 提交/重置按钮
    rightButtonIconId = R.drawable.ic_double_arrow_right_24dp,  // 下一题/完成按钮
    playPron = { playPron(word.pronName) }  // 发音按钮
)

3.最终设计界面如下所示:

三、单词练习功能的开发

1.页面布局:页面主要由四个选项组成,每个选项对应一个单词释义,用户通过点击相应的选项完成作答,此部分由ChoiceScreen完成。当用户做出选择后,页面跳转,通过不同颜色显示答案的正确与否,并根据不同主题模式设置了不同颜色,界面如下:

四、单词默写练习功能开发 

1.动态区域设计:在页面的底部,设计了动态区域,根据 uiState.submitted 的状态驱动ui。

未提交时,显示键盘:

LandingKeyboard(
    write = write,  // 字母按键回调
    remove = remove // 删除键回调
)

提交后,显示答案:

Column {
    // 正确答案
    Text(uiState.word.spelling, style = MaterialTheme.typography.headlineLarge)
    
    // 音标
    Text(uiState.word.ipa, color = MaterialTheme.colorScheme.outline)
    
    // 对错图标
    Icon(
        painter = if (uiState.input == uiState.word.spelling) 
            R.drawable.ic_correct_24dp 
        else 
            R.drawable.ic_close_24dp
    )
}

2.局部弹窗设计:在 Composable 函数中根据 dialog 状态显示不同弹窗,弹窗状态与主状态绑定,避免不同步,代码如下:

@Composable
fun SpellingScreen(viewModel: SpellingViewModel) {
    val uiState by viewModel.uiState.collectAsState()
    
    if (uiState is SpellingUiState.Success) {
        when (uiState.dialog) {
            is SpellingUiState.Success.Dialog.None -> Unit // 不显示
            is SpellingUiState.Success.Dialog.Processing -> {
                LoadingDialog(onDismiss = viewModel::closeDialog)
            }
            is SpellingUiState.Success.Dialog.Dictionary -> {
                val word = uiState.dialog.word
                WordDetailDialog(
                    word = word,
                    onDismiss = viewModel::closeDialog
                )
            }
        }
    }
}

3.界面开发如下:

五、错题列表功能开发

1.错题列表主要通过弹窗显示释义,此设计更贴合用户使用习惯。通过openDictionaryDialog方法进行条件检查,clickedWrongWord:保存当前选中的单词对象(用于弹窗内容展示),dialog:将弹窗状态设为 Dictionary 类型(触发UI渲染弹窗)。而closeDialog 方法关闭当前显示的任何弹窗(包括词典弹窗或处理中弹窗)。

// 打开前:
Success(
    showWrongList = true,
    clickedWrongWord = null,
    dialog = Dialog.None
)

// 打开后:
Success(
    showWrongList = true,
    clickedWrongWord = word,  // 实际单词对象
    dialog = Dialog.Dictionary
)

2.交互流程如下:

User->>UI: 点击错词按钮
    UI->>ViewModel: 调用 openDictionaryDialog(word)
    ViewModel->>UI: 更新状态显示词典弹窗

六、实时同步学习进度(可视化展示)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cherlin_zy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值