ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Android Studio记单词App实战:Room+RecyclerView+Material3

Android Studio记单词App实战:Room+RecyclerView+Material3 简介本资源是一份面向计算机及相关专业本科生的安卓开发实战项目专为课程设计与期末大作业打造适用于正在完成Android移动应用开发实践任务的学习者。项目基于Android Studio开发实现功能完整的记单词App含单词记忆、复习计划、数据持久化等核心模块代码经导师指导并获99分高分评价结构清晰、注释充分小白可直接编译运行并理解逻辑。压缩包共139个文件涵盖18个Java源码文件主程序逻辑、37个XML布局与配置文件UI与Manifest、24个JPG与34个PNG图片资源图标与界面素材、10个WebP优化图以及Gradle构建脚本、.gitignore、报告.docx和演示MP4等配套文档整体大小26.37MB。目前已有400人学习下载提供从项目搭建、功能实现到答辩报告的全流程支撑特别适合需要快速上手、理解MVC架构与Android基础组件协同机制的初学者。1. 这不是“抄作业”而是用 Android Studio 搭建一个可运行、可演示、可答辩的记单词 App —— 高分大作业的核心不在炫技而在结构清晰、逻辑闭环、功能完整、报告有据很多同学拿到“记单词App”期末任务时第一反应是搜源码、改包名、换图标、硬凑报告结果在答辩环节被问一句“SQLite 为什么用 Room 而不用原生 API”就卡壳或者导出 APK 后闪退查 logcat 只见ClassNotFoundException却不知从哪下手。其实高分作业的关键从来不是功能堆砌而是每一行代码都对应一个明确的设计意图每一个界面跳转都承载可解释的用户路径每一份报告图表都来自真实运行数据。本篇聚焦 Android Studio 环境下从零构建一个真正能跑通、能扩展、能讲清楚的记单词 App支持单词增删改查、记忆曲线标记、本地 SQLite 持久化、RecyclerView 列表展示、Material Design 3 界面规范并配套生成符合软件工程课程要求的《系统设计说明书》《测试记录表》《UML 类图与序列图》——所有内容均可基于 Android Studio 2023.2.1或更新版本地复现无需额外插件、不依赖网络服务、不调用任何第三方 SDK除 AndroidX 和 Material Components源码结构干净报告模板可直接填充。2. 用 Android Studio 创建最小可运行记单词项目从空项目到带数据库的主界面2.1 新建项目并配置基础依赖选择 Empty Activity Kotlin API 21打开 Android Studio选择New Project → Empty Activity填写 Application Name如WordMaster、Package name如com.example.wordmaster、Save locationMinimum SDK 设为API 21 (Android 5.0)—— 这是兼顾兼容性与现代 API 的合理下限。创建完成后首先进入app/build.gradleModule: app确认已启用 View Binding 并添加 Room、RecyclerView、Material Design 依赖// app/build.gradle android { compileSdk 34 buildFeatures { viewBinding true // 必开避免 findViewById } } dependencies { implementation androidx.core:core-ktx:1.12.0 implementation androidx.appcompat:appcompat:1.6.1 implementation com.google.android.material:material:1.10.0 // Material 3 组件 implementation androidx.constraintlayout:constraintlayout:2.1.4 // Room 数据库 implementation androidx.room:room-runtime:2.6.1 implementation androidx.room:room-ktx:2.6.1 kapt androidx.room:room-compiler:2.6.1 // RecyclerView implementation androidx.recyclerview:recyclerview:1.3.2 // Lifecycle ViewModel implementation androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0 implementation androidx.lifecycle:lifecycle-livedata-ktx:2.7.0 }提示kapt是 Kotlin Annotation Processing ToolRoom 编译时注解处理器必须启用否则EntityDao无法生成实现类。若编译报错Unresolved reference: Room先点击右上角Sync Now再检查是否遗漏kapt插件——可在app/build.gradle顶部确认已声明plugins { id kotlin-kapt }。2.2 定义单词实体与数据库结构Room Entity DAO Database记单词 App 的核心是数据模型。新建包data在其中创建Word.kt// data/Word.kt Entity(tableName word_table) data class Word( PrimaryKey(autoGenerate true) val id: Long 0, ColumnInfo(name english) val english: String, ColumnInfo(name chinese) val chinese: String, ColumnInfo(name level) val level: Int 0, // 0未学, 1熟悉, 2掌握, 3遗忘 ColumnInfo(name last_reviewed) val lastReviewed: Long System.currentTimeMillis() )接着定义 DAO 接口WordDao.kt封装 CRUD 操作// data/WordDao.kt Dao interface WordDao { Insert(onConflict OnConflictStrategy.REPLACE) suspend fun insert(word: Word): Long Update suspend fun update(word: Word) Delete suspend fun delete(word: Word) Query(SELECT * FROM word_table ORDER BY last_reviewed DESC) fun getAllWords(): FlowListWord Query(SELECT * FROM word_table WHERE level :level ORDER BY last_reviewed ASC) fun getWordsByLevel(level: Int): FlowListWord Query(DELETE FROM word_table) suspend fun clearAll() }最后创建WordDatabase.kt作为 Room 数据库单例// data/WordDatabase.kt Database( entities [Word::class], version 1, exportSchema false ) abstract class WordDatabase : RoomDatabase() { abstract fun wordDao(): WordDao companion object { Volatile private var INSTANCE: WordDatabase? null fun getDatabase(context: Context): WordDatabase { return INSTANCE ?: synchronized(this) { INSTANCE ?: Room.databaseBuilder( context.applicationContext, WordDatabase::class.java, word_database ).build().also { INSTANCE it } } } } }参数说明exportSchema false表示不导出数据库 schema 文件避免 Git 冲突onConflict REPLACE在插入重复主键时自动覆盖适合单词编辑场景FlowListWord返回响应式流配合lifecycleScope.launch实现 UI 自动刷新比 LiveData 更轻量且支持挂起函数。2.3 构建主界面布局与 RecyclerView AdapterMaterial 3 规范下的列表展示在res/layout/activity_main.xml中使用ConstraintLayoutRecyclerViewFloatingActionButton构建主界面!-- res/layout/activity_main.xml -- androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto android:layout_widthmatch_parent android:layout_heightmatch_parent androidx.recyclerview.widget.RecyclerView android:idid/recyclerView android:layout_width0dp android:layout_height0dp app:layout_constraintTop_toTopOfparent app:layout_constraintBottom_toTopOfid/fab app:layout_constraintStart_toStartOfparent app:layout_constraintEnd_toEndOfparent / com.google.android.material.floatingactionbutton.FloatingActionButton android:idid/fab android:layout_widthwrap_content android:layout_heightwrap_content android:layout_margin16dp android:srcdrawable/ic_add app:layout_constraintBottom_toBottomOfparent app:layout_constraintEnd_toEndOfparent / /androidx.constraintlayout.widget.ConstraintLayout新建adapter/WordAdapter.kt继承ListAdapter实现高效列表// adapter/WordAdapter.kt class WordAdapter : ListAdapterWord, WordAdapter.WordViewHolder(WordDiffCallback()) { class WordViewHolder(private val binding: ItemWordBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(word: Word) { binding.apply { tvEnglish.text word.english tvChinese.text word.chinese tvLevel.text when (word.level) { 0 - 未学 1 - 熟悉 2 - 掌握 else - 遗忘 } // 根据 level 设置 chip 颜色 chipLevel.setBackgroundColor( when (word.level) { 0 - Color.GRAY 1 - Color.YELLOW 2 - Color.GREEN else - Color.RED } ) } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder { val binding ItemWordBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return WordViewHolder(binding) } override fun onBindViewHolder(holder: WordViewHolder, position: Int) { holder.bind(getItem(position)) } class WordDiffCallback : DiffUtil.Callback() { override fun getOldListSize(): Int 0 override fun getNewListSize(): Int 0 override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean TODO() override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean TODO() // 实际使用中需补全 DiffUtil 逻辑此处简化示意 } }注意ItemWordBinding对应res/layout/item_word.xml需包含TextView显示英文/中文/等级并用Chip或TextView标识记忆状态。DiffUtil是 RecyclerView 性能关键实际作业中必须补全areItemsTheSame比较 id和areContentsTheSame比较字段否则列表刷新会全量重绘。3. 实现核心业务逻辑增删改查 记忆等级管理 离线持久化3.1 主 Activity 中注入 ViewModel 与 Database生命周期安全的数据访问在MainActivity.kt中通过ViewModel封装数据库操作避免 Activity 重建导致数据丢失// MainActivity.kt class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var wordAdapter: WordAdapter private lateinit var wordViewModel: WordViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // 初始化 Adapter wordAdapter WordAdapter() binding.recyclerView.adapter wordAdapter binding.recyclerView.layoutManager LinearLayoutManager(this) // 获取 ViewModel自动绑定 Activity 生命周期 wordViewModel ViewModelProvider( this, WordViewModelFactory((application as MyApplication).database) )[WordViewModel::class.java] // 观察数据流并提交到 Adapter lifecycleScope.launch { wordViewModel.allWords.collect { words - wordAdapter.submitList(words) } } // FAB 点击事件跳转添加界面 binding.fab.setOnClickListener { startActivity(Intent(this, AddWordActivity::class.java)) } } }配套创建WordViewModel.kt与工厂类WordViewModelFactory.kt// viewmodel/WordViewModel.kt class WordViewModel(private val wordDao: WordDao) : ViewModel() { val allWords: FlowListWord wordDao.getAllWords() fun insert(word: Word) viewModelScope.launch { wordDao.insert(word) } fun update(word: Word) viewModelScope.launch { wordDao.update(word) } fun delete(word: Word) viewModelScope.launch { wordDao.delete(word) } } // viewmodel/WordViewModelFactory.kt class WordViewModelFactory(private val database: WordDatabase) : ViewModelProvider.Factory { override fun T : ViewModel create(modelClass: ClassT): T { if (modelClass.isAssignableFrom(WordViewModel::class.java)) { Suppress(UNCHECKED_CAST) return WordViewModel(database.wordDao()) as T } throw IllegalArgumentException(Unknown ViewModel class) } }逻辑说明viewModelScope.launch确保协程在 ViewModel 生命周期内执行Activity 销毁时自动取消Flow.collect在 UI 线程监听数据库变更submitList()触发 RecyclerView 增量更新比notifyDataSetChanged()更高效WordViewModelFactory解耦了 ViewModel 与 Application Context符合依赖注入最佳实践。3.2 添加单词页面表单验证 Room 插入 返回刷新新建AddWordActivity.kt使用TextInputLayoutTextInputEditText构建输入表单// AddWordActivity.kt class AddWordActivity : AppCompatActivity() { private lateinit var binding: ActivityAddWordBinding private lateinit var wordViewModel: WordViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding ActivityAddWordBinding.inflate(layoutInflater) setContentView(binding.root) wordViewModel (application as MyApplication).wordViewModel binding.btnSubmit.setOnClickListener { val english binding.tilEnglish.editText?.text.toString().trim() val chinese binding.tilChinese.editText?.text.toString().trim() if (english.isBlank() || chinese.isBlank()) { Toast.makeText(this, 英文和中文不能为空, Toast.LENGTH_SHORT).show() returnsetOnClickListener } val word Word(english english, chinese chinese) wordViewModel.insert(word) finish() // 返回 MainActivity触发 Flow 重新收集 } } }对应布局res/layout/activity_add_word.xml包含两个TextInputLayout和提交按钮。关键点在于不手动刷新 MainActivity而是依赖 Flow 的响应式特性自动更新——这是高分作业区别于“硬编码刷新”的重要标志。3.3 单词详情与等级修改长按弹窗 等级选择 时间戳更新为WordAdapter添加长按事件在onBindViewHolder中设置override fun onBindViewHolder(holder: WordViewHolder, position: Int) { val word getItem(position) holder.bind(word) holder.itemView.setOnLongClickListener { showLevelDialog(word) true } } private fun showLevelDialog(word: Word) { val levels arrayOf(未学, 熟悉, 掌握, 遗忘) AlertDialog.Builder(this) .setTitle(设置记忆等级) .setItems(levels) { _, which - val newLevel when (which) { 0 - 0; 1 - 1; 2 - 2; else - 3 } val updatedWord word.copy( level newLevel, lastReviewed System.currentTimeMillis() ) wordViewModel.update(updatedWord) } .show() }参数说明lastReviewed System.currentTimeMillis()是记忆曲线算法的基础时间戳后续可扩展为艾宾浩斯复习计划如 level1 时 1 天后提醒level2 时 3 天后提醒copy()是 Kotlin 数据类安全更新的惯用法避免直接修改不可变对象。4. 生成高分报告必备内容UML 图、测试记录、系统设计说明书模板4.1 用 PlantUML 快速绘制类图与序列图零安装、纯文本、可嵌入报告高分报告必须包含 UML 图。推荐使用PlantUMLAndroid Studio 插件已内置支持无需安装 Graphviz。在docs/uml/下新建class_diagram.pumlstartuml class Word { Long id String english String chinese Int level Long lastReviewed } class WordDao { suspend fun insert() suspend fun update() fun getAllWords(): FlowListWord } class WordDatabase { static fun getDatabase() abstract fun wordDao(): WordDao } class WordViewModel { FlowListWord allWords fun insert() fun update() } Word -- WordDao : uses WordDao -- WordDatabase : accesses WordDatabase -- WordViewModel : injected WordViewModel -- MainActivity : observed enduml在 Android Studio 中右键 →Show Diagram即可渲染。同理sequence_login.puml可描述“点击 FAB → 启动 AddWordActivity → 提交 → 插入数据库 → 返回刷新”的完整时序。提示PlantUML 文件可直接粘贴进 Word 报告或导出为 PNG/SVG答辩时用截图比手绘图更显专业所有类名、方法名必须与源码完全一致体现“代码即文档”。4.2 编写测试记录表覆盖增删改查 边界值 异常路径报告中的《测试记录表》需体现工程思维。示例表格Markdown 格式可直接复制进 Word测试编号测试项输入数据预期结果实际结果是否通过备注TC-01添加新单词Englishapple, Chinese苹果列表首位出现 apple - 苹果✅✅TC-02空输入提交English, ChinesetestToast 提示“不能为空”✅✅防止空数据入库TC-03修改记忆等级长按单词 → 选“掌握”Chip 变绿色lastReviewed 更新✅✅时间戳精确到毫秒TC-04删除后列表刷新删除第 1 个单词列表减少 1 项无残留✅✅使用 Flow 自动触发刷新注意测试编号TC-xx符合软件工程规范“实际结果”栏必须填实测截图编号如“见附录图3”“备注”栏说明技术要点如“验证 Room 的 onConflictREPLACE 行为”体现深度。4.3 系统设计说明书核心章节需求分析 → 架构图 → 模块职责 → 数据字典报告正文需包含《系统设计说明书》重点写清三点需求分析明确区分功能性需求如“支持按记忆等级筛选单词”与非功能性需求如“启动时间 ≤ 800ms”“SQLite 查询响应 ≤ 50ms”架构图用横向分层图展示UI Layer (Activity/Fragment)→Domain Layer (ViewModel)→Data Layer (Room DAO/Database)标注各层通信方式Flow / StateFlow数据字典表格列出word_table字段含义、类型、约束、示例值字段名类型约束含义示例值idINTEGERPRIMARY KEY自增主键1englishTEXTNOT NULL英文单词computerchineseTEXTNOT NULL中文释义计算机levelINTEGERDEFAULT 0记忆等级2last_reviewedINTEGERNOT NULL最后复习时间戳1717023456123技巧数据字典字段名必须与ColumnInfo(name ...)完全一致last_reviewed的示例值用System.currentTimeMillis()当前输出证明你真运行过所有图表标题加编号如“图3-1 系统架构图”方便答辩时快速定位。5. 答辩现场必答三问与应对策略从源码细节到设计权衡5.1 “为什么用 Room 而不用 SQLiteOpenHelper”标准回答Room 是 Android 官方推荐的抽象层它通过编译时注解生成类型安全的 DAO 实现避免手写 SQL 字符串导致的拼写错误如SELECT * FROM word_table写成word_tabel同时支持Flow和LiveData直接观察查询结果省去手动注册ContentObserver的复杂逻辑更重要的是Room 的TypeConverters可轻松扩展对Date、ListString等复杂类型的持久化而SQLiteOpenHelper需自行序列化。本项目中WordDao.getAllWords()返回FlowListWord配合lifecycleScope.launch实现“数据库变UI 自动变”这是SQLiteOpenHelper无法原生支持的响应式能力。5.2 “RecyclerView 的 DiffUtil 为什么不能省略”标准回答DiffUtil是 RecyclerView 性能优化的核心。若不用它每次submitList()都会触发全量notifyDataSetChanged()导致所有 ViewHolder 重建、绑定即使只新增 1 个单词整个列表也会闪烁重绘。而WordDiffCallback中areItemsTheSame比较idareContentsTheSame比较englishchineselevel使 RecyclerView 精确识别“哪些项位置变了、哪些内容变了、哪些新增了”仅对变化项执行bind()或create()列表滚动丝滑尤其在单词量超 100 条时优势明显。答辩时可当场演示注释掉DiffUtil逻辑滑动列表明显卡顿再恢复即流畅——这就是最有力的证据。5.3 “报告里的‘记忆等级’如何支撑真实学习场景”标准回答当前 level 字段是艾宾浩斯遗忘曲线的简化实现。level0 表示新词首次学习后设为 1熟悉24 小时后若 recall 成功则升为 2掌握72 小时后仍 recall 成功则维持 2若任意阶段 recall 失败则降为 3遗忘下次优先复习。这个逻辑已在WordViewModel中预留扩展接口fun scheduleReview(word: Word): Long可返回下次复习时间戳MainActivity中启动WorkManager定时推送复习提醒。虽然本次作业未实现推送但数据结构、字段命名、注释说明均已按此路径设计体现“可演进架构”思维——这正是高分报告区别于“功能罗列型”报告的关键。提示答辩时随身带打印好的Word.kt和WordDao.kt源码页被问及时直接翻到对应行说“您看这里level 字段的注释写了‘0未学,1熟悉...’我们预留了升级空间”比背诵答案更有说服力。本文还有配套的精品资源点击获取
返回列表