ARTICLE DETAIL

资讯详情

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

Android Studio单词本APP源码实战:Gradle适配与SQLite存储详解

Android Studio单词本APP源码实战:Gradle适配与SQLite存储详解 简介这是一个基于Android Studio开发的单词本App完整源码主要面向安卓期末大作业、课程设计或毕业设计的学生对希望快速拿到可运行项目的读者尤其适用。项目实现单词录入、分类浏览、查询记忆等实用功能采用Java编写业务逻辑、XML定义界面布局、SQLite实现本地数据存储整体结构清楚规范导入Android Studio后即可编译运行无需修改。压缩包共275个文件、约6.36MB核心包括155个XML布局/配置文件与75个Java源文件另有11张JPG界面图、7张PNG图标、7个JAR依赖包、2个WAV提示音、1个now_word.db数据库以及build.gradle、settings.gradle、gradlew等Gradle工程配置目录完整。学习时可通过Java源码追踪Activity页面跳转与事件处理借助XML布局理解控件组织方式查看now_word.db掌握SQLite建库与增删改查。目前已有3036人学习下载。这份源码既能直接作为期末项目方案也适合对照研读安卓界面交互、数据持久化和资源管理方式避开常见编译与配置问题。1. 这门期末大作业到底值不值得打开先想清楚你要交什么又到了安卓课设截止前一周GitHub 刷了几十页收藏了十几个 “XXX 安卓源码 期末大作业”大多数下载下来不是缺依赖就是跑不起来。这个标题是“基于 Android Studio 开发的单词本 APP 源码”说明它至少是 Android Studio 工程不是 Eclipse 老古董也不是套了个网页壳的 Cordova 项目。单词本这个选题的好处是功能边界清晰背单词、查单词、生词本、记忆统计从数据存储到列表展示到界面跳转安卓课设该覆盖的知识点它基本都碰了一遍又不至于像商城或新闻客户端那样动辄十几个 Activity 撑起来。这篇笔记适合两类人一类是零基础只想把项目跑起来看清代码结构后换皮交差另一类是已经会写基础页面想在这个源码上做二次开发把“单词本”扩成带记忆曲线的背单词工具。我先说结论这个源码能跑通不难难点在导入时的 Gradle 适配和后续改造时对 SQLite、RecyclerView 的理解。如果你现在连 Android Studio 都没装明白下面第 2 章先带你走通“移植 Android Studio 项目”的全流程。2. 把源码从 zip 变成能跑的 App导入、Gradle 适配与目录结构2.1 解压后先别急着打开先看清工程家底拿到“基于 Android Studio 开发的单词本 APP 源码.zip”第一步不是双击 Android Studio而是解压后看顶层目录。一个标准 Android Studio 工程至少有 settings.gradle、build.gradle、app 模块这三个要素。如果解压后直接看到一堆 .java 文件而没有 gradle 相关配置那说明这不是能直接导入的 Android Studio 工程得自己新建项目再把代码搬进去。我见过不少同学下载的“安卓期末大作业源码”其实只是把 Java 文件打成包根本不含 Gradle 配置那种项目的移植成本会比你想的高很多。正常的工程结构里你应该能看到 app/src/main/java 下的包名目录、app/src/main/res 里的布局和资源、app/src/main/AndroidManifest.xml以及 app/build.gradle。命令行进到工程根目录执行ls确认这几个文件存在再继续省得导入到一半发现缺文件。如果确实有完整 Gradle 配置直接进入下一步。# 解压 zip注意路径不要带空格Android Studio 对中文路径支持不稳定 unzip wordbook_app.zip -d ~/AndroidProjects/ cd ~/AndroidProjects/wordbook_app # 查看顶层关键文件 ls -la settings.gradle build.gradle app/build.gradle参数说明解压目标目录建议放在用户主目录下避免放在/root或其他权限受限目录命令行里wordbook_app只是示意实际以你解压后的文件夹名为准。如果settings.gradle里声明了多个模块例如include :app, :library那说明这是个多模块工程导入时耗时会更长但单词本这种小项目通常只有一个app模块。2.2 导入 Android Studio 的三个关键步骤打开 Android Studio选 Open找到解压后的目录点 OK。这时候 Gradle Sync 会自动触发。绝大多数“移植 Android Studio 项目”翻车都发生在这一步要么一直卡在下载 Gradle 发行版要么报错提示 SDK 版本不存在。先说最常见的处理方式打开gradle/wrapper/gradle-wrapper.properties看gradle-7.4.2-bin.zip这行然后对比你本机 Android Studio 的版本。// gradle/wrapper/gradle-wrapper.properties 关键行 distributionUrlhttps\://services.gradle.org/distributions/gradle-7.4.2-bin.zip如果这个版本在你本机一直同步失败换成你本机 Android Studio 已自带的 Gradle 版本一般 Android Studio Dolphin 或 Electric Eel 对应 7.x 都能跑。Gradle 版本不是越高越好要和app/build.gradle里的compileSdk、targetSdk搭配。尽量优先用工程自带的版本下载失败再换不要一开始就乱改。提示同步前先在 Settings 里确认 SDK Location 指向你已经安装的 Android SDK。很多人卡在“SDK 组件缺失”是因为 Android Studio 装好后没下载对应 API Level 的 Platform。2.3 语言与界面把 Android Studio 设置成中文的常见做法热词里“Android Studio 怎么设置中文”追问很多这里顺带讲掉。Android Studio 自带中文语言包只是默认没启用。打开Settings/Plugins搜索Chinese (Simplified) Language Pack安装后重启就是中文界面。这个不影响工程编译只是 IDE 界面语言。但我不建议一上来就切中文。原因有二你交上去的源码注释、报错信息全是英文切了界面中文但报错还是英文反而中间多一道翻译的脑回路期末大作业答辩时老师可能现场改代码你操作界面跟老师口中的“打开 Manifest 文件”对不上。等跑通之后你爱怎么切怎么切别在入门阶段给自己添障碍。2.4 跑起来模拟器与真机的选择点击 Run 按钮前先决定跑到哪。一般同意你选模拟器或真机。模拟器的坑在于 ARM 转译导致的启动慢单词本这种轻量 App 还好但如果你电脑内存只有 8G建议直接用真机调试。手机开启开发者选项USB 连接后选择文件传输模式Android Studio 会自动识别设备。# 查看 Android Studio 是否识别到设备命令行工具目录要配到 PATH adb devices如果列表为空多半是驱动没装或手机没授权拔掉重插手机弹窗点允许。真机跑的好处是 SQLite 数据库可以直接用adb shell进到应用私有目录查看到底有没有写进去这对后面排查数据不显示类问题非常关键。3. 单词本的核心功能是怎么实现的从数据存储到列表展示3.1 为什么用 SQLite 而不是 Room期末项目的技术选择这种课程设计级别的单词本源码绝大多数是用 SQLite 原生 API 写的少数会用 Room。原因不是 Room 不好而是期末大作业考察点通常在 SQLiteOpenHelper 的使用、SQL 语句的编写、ListView 或 RecyclerView 的适配器用 Room 会把这些知识点封装掉答辩时老师一问三不知反而扣分。所以看到代码里直接出现SQLiteOpenHelper时先别觉得“不够现代”它是课设标准答案。Room 是谷歌官方的 ORM 封装能用但交给答辩不好讲。SQLite 裸写虽然模板代码多但每一步都对应一个明确的安卓知识点onCreate建表、getWritableDatabase拿数据库、Cursor遍历结果集。如果你拿到的源码用的是 Room也别慌功能上是等价的改起来反而更快。// WordDbHelper.java —— 核心建表与增删改查 public class WordDbHelper extends SQLiteOpenHelper { // 数据库版本号升级表结构时 1会触发 onUpgrade private static final int DB_VERSION 1; private static final String DB_NAME wordbook.db; public WordDbHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } Override public void onCreate(SQLiteDatabase db) { // 单词表自增主键 单词 释义 是否已收藏 String sql CREATE TABLE IF NOT EXISTS words ( id INTEGER PRIMARY KEY AUTOINCREMENT, word TEXT NOT NULL, meaning TEXT NOT NULL, favorite INTEGER DEFAULT 0); db.execSQL(sql); } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 简化处理直接删表重建。有真实用户数据时绝不要这么做 db.execSQL(DROP TABLE IF EXISTS words); onCreate(db); } }这段代码的逻辑说明SQLiteOpenHelper是安卓提供的数据库辅助类onCreate只会在数据库文件第一次创建时执行一次。把建表语句放在这里的意义是你不需要在MainActivity里手动判断表是否存在。favorite字段用INTEGER而非BOOLEAN是因为 SQLite 没有原生布尔型0/1 是惯例。onUpgrade里的删除重建只适合开发阶段等你要保留用户数据时必须改成增量迁移不然用户更新 App 后生词本全没了。3.2 RecyclerView 展示单词列表Adapter 的三个重写方法单词表建好后界面上要把数据倒出来。源码里大概率用了 RecyclerView少数老工程用 ListView。如果你看到的是 ListView建议换成 RecyclerView理由很现实答辩时老师会问“为什么用 RecyclerView 不用 ListView”你要答出“ViewHolder 复用机制”。如果源码本来就用 RecyclerView你直接看 Adapter 的onCreateViewHolder、onBindViewHolder、getItemCount三个方法即可。// WordAdapter.java —— RecyclerView 适配器核心 public class WordAdapter extends RecyclerView.AdapterWordAdapter.WordViewHolder { private ListWord wordList; private OnWordClickListener listener; public WordAdapter(ListWord wordList, OnWordClickListener listener) { this.wordList wordList; this.listener listener; } NonNull Override public WordViewHolder onCreateViewHolder(NonNull ViewGroup parent, int viewType) { // 把 item_word.xml 布局文件转成 View 对象 View view LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_word, parent, false); return new WordViewHolder(view); } Override public void onBindViewHolder(NonNull WordViewHolder holder, int position) { Word word wordList.get(position); holder.tvWord.setText(word.getWord()); holder.tvMeaning.setText(word.getMeaning()); // 点击整个 item 回调方便做单词详情跳转 holder.itemView.setOnClickListener(v - listener.onClick(word)); } Override public int getItemCount() { return wordList null ? 0 : wordList.size(); } static class WordViewHolder extends RecyclerView.ViewHolder { TextView tvWord, tvMeaning; WordViewHolder(NonNull View itemView) { super(itemView); tvWord itemView.findViewById(R.id.tv_word); tvMeaning itemView.findViewById(R.id.tv_meaning); } } }参数说明wordList是数据源一般由Presenter或Activity从数据库查询后塞进来。onCreateViewHolder只负责创建视图容器onBindViewHolder负责把数据绑定到控件上二者分离就是 RecyclerView 能高性能滑动的关键。ViewHolder把findViewById的结果缓存起来避免每次滚动都重新查找控件。这里我用的是 lambda 表达式写法如果你的源码是 Java 7 语法改成匿名内部类即可。3.3 搜索与增删改SearchView 和 Dialog 的搭配单词本不能光有列表得有搜索。常见的实现是用 SearchView 或 EditText 监听TextWatcher每次文本变化就查一次数据库。这里有一个性能边界要提醒你单词库如果是几千条每次都LIKE %keyword%全表扫描没问题如果以后扩展到 10 万条就必须上 FTS4 全文索引了。期末作业层面LIKE 查询够用。// MainActivity 中搜索监听的关键写法 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { Override public boolean onQueryTextSubmit(String query) { // 点击键盘搜索键时触发这里可以加载更多但单词本直接过滤即可 queryWords(query.trim()); return false; } Override public boolean onQueryTextChange(String newText) { // 每次输入变化都触发注意 trim 掉首尾空格避免查询出空条件 queryWords(newText.trim()); return false; } }); private void queryWords(String keyword) { SQLiteDatabase db dbHelper.getReadableDatabase(); Cursor cursor; if (keyword.isEmpty()) { cursor db.query(words, null, null, null, null, null, word ASC); } else { cursor db.query(words, null, word LIKE ?, new String[]{% keyword %}, null, null, word ASC); } ListWord result new ArrayList(); while (cursor.moveToNext()) { Word word new Word(); word.setId(cursor.getLong(cursor.getColumnIndexOrThrow(id))); word.setWord(cursor.getString(cursor.getColumnIndexOrThrow(word))); word.setMeaning(cursor.getString(cursor.getColumnIndexOrThrow(meaning))); result.add(word); } cursor.close(); adapter.setWordList(result); }这段代码有两点值得说。一是getColumnIndexOrThrow比getColumnIndex安全列不存在会直接抛异常开发期就能发现字段名拼错而不是运行期拿到 -1 再崩。二是cursor.close()千万别省Cursor 是持有数据库连接资源的对象不关闭会泄漏。很多同学跑起来没问题但反复搜索后越来越卡就是反复打开游标没关。数据库连接用完也要关可以用finally或try-with-resources包裹。4. 移植与运行中逃不掉的 5 个经典坑现象、原因、解决4.1 Gradle 同步一直是“Loading”半小时不动现象导入工程后底部状态栏一直转圈或者提示“Could not resolve com.android.tools.build:gradle:xxx”。原因Gradle 发行版或依赖仓库下载不走国内镜像默认从services.gradle.org和google()仓库拉取网络不通或超时。解决先看gradle-wrapper.properties里的版本在命令行手动下载对应 zip 包放到~/.gradle/wrapper/dists/对应目录更省事的做法是在工程根目录build.gradle里把仓库换成阿里云镜像// 工程根目录 build.gradle 中替换仓库源 buildscript { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/gradle-plugin } mavenCentral() } } allprojects { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/public } mavenCentral() } }4.2 RecyclerView 页面白屏一个字都不显示现象App 能启动单词列表区域空白。原因八成是忘记给 RecyclerView 调用setLayoutManager或者数据库查询返回空数据。前者是布局渲染问题后者是业务逻辑问题。解决在onCreate里加上recyclerView.setLayoutManager(new LinearLayoutManager(this))确认dbHelper的onCreate真的执行过。判断数据是否存在最简单的方式是用adb shell进到应用数据目录下找数据库文件# 写入测试数据后用命令行确认数据库真的建出来了 adb shell run-as com.example.wordbook ls databases/如果databases目录下没有wordbook.db说明onCreate没触发或者你的 App 还没执行到任何数据库操作。再往上一层排查包名是否正确是不是用错了context。4.3 插入中文后界面显示乱码现象界面上单词释义显示成“???”但数据库文件里直接查是正常中文。原因多半不是编码问题而是Cursor或ListView渲染时把byte[]当字符串处理了或者布局里TextVIew的textSize太小看不出字。更常见的是你从 CSV 或 Excel 导入单词时文件本身是 GBK 编码读进 Java 字符串后没转 UTF-8。解决导入数据时统一用 UTF-8 读取。如果源码里有new String(bytes, GBK)这种硬编码编码格式的地方改掉。SQLite 本身是 UTF-8 存储只要 Java 字符串正确写入读出都不会乱码。4.4 点击某个按钮直接闪退Logcat 报 ActivityNotFoundException现象App 启动正常跳转第二个页面时崩溃错误信息提示“Unable to find explicit activity class”。原因在AndroidManifest.xml里忘了注册新写的 Activity。IDE 自动生成的页面会帮你注册但手动新建的 Activity 类经常漏。解决打开app/src/main/AndroidManifest.xml检查application节点下有没有对应 Activity 的activity声明没有就补上activity android:name.WordDetailActivity /注意.WordDetailActivity前面的点是相对包名的简写如果你工程包名是com.example.wordbook等价于com.example.wordbook.WordDetailActivity。补完后 Clean Project 再重新 Run。4.5 App 重启后收藏的单词全部消失现象第一次运行收藏了几个词杀掉 App 重进收藏状态没了。原因收藏状态写在了内存变量里或者用了SharedPreferences但 key 写错又或者收藏字段更新了内存集合但没有同步到 SQLite。解决把收藏状态落库。favorite字段已经建好了那更新时就要调用 SQLite 的update方法同时改动内存里的wordList并notifyDataSetChanged。两处都更新才靠谱。只改界面不写库或者只写库不刷新界面是这类 bug 的两大来源。5. 把期末大作业改造成“有亮点”的单词本三个低成本扩展方向5.1 加一个生词本 Tab复用现有表结构别新开数据库很多单词本 App 的“生词本”其实是独立 Activity 或 Fragment。但在期末作业层面最稳的扩展方式是给words表加一个favorite字段过滤查询前面建表时已经预留了。你在主界面加一个TabLayout ViewPager2分成“全部单词”和“收藏单词”两个页签收藏页只查询favorite 1的数据。// 查询收藏单词只需一行 where 条件 Cursor favoriteCursor db.query(words, null, favorite ?, new String[]{1}, null, null, updated_at DESC);改动成本很小但答辩时你可以说“实现了生词分类管理基于数据库查询优化”这就比单纯列表多了一个可以讲的功能点。注意favorite字段的默认值在插入数据时要显式指定为 0不然老数据可能为 NULL导致favorite ?匹配不到。5.2 背单词打卡统计SharedPreferences 比 SQLite 更合适打卡功能的关键是记录“今天背了哪些单词”“连续打卡几天”。这类轻量数据不需要建表用SharedPreferences存三个值last_date上次打卡日期、continuous_days连续天数、total_words累计背词数。核心判断逻辑写在WordBookApplication或工具类里public class CheckInManager { private static final String PREFS_NAME checkin; private static final String KEY_LAST_DATE last_date; private static final String KEY_CONTINUOUS_DAYS continuous_days; public static void recordCheckIn(Context context) { SharedPreferences sp context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); String today new SimpleDateFormat(yyyy-MM-dd, Locale.getDefault()).format(new Date()); String lastDate sp.getString(KEY_LAST_DATE, ); int days sp.getInt(KEY_CONTINUOUS_DAYS, 0); if (today.equals(lastDate)) { return; // 今天已打卡不能重复加天数 } days isYesterday(lastDate, today) ? days 1 : 1; sp.edit().putString(KEY_LAST_DATE, today).putInt(KEY_CONTINUOUS_DAYS, days).apply(); } }这个功能的重点是展示对日期处理的理解而不是界面多复杂。答辩时老师问“为什么用 SharedPreferences 不用数据库”你可以答“这类键值型数据无关联查询需求用文件存储避免无谓的数据库开销”这就是一个加分回答。5.3 换成卡片式背诵界面ViewPager2 FragmentStateAdapter如果想给界面增加视觉亮点把单列表改成卡片式滑动背单词用ViewPager2配合FragmentStateAdapter。每页显示一个单词卡片左滑标记“不认识”右滑标记“认识”记录到内存列表里。这个改动有几个边界要留意。ViewPager2默认会预加载相邻页面OFFSCREEN_PAGE_LIMIT参数要显式设置成 1不然词汇量一大就会出现卡顿。不要继承FragmentPagerAdapter它会把所有 Fragment 都保存在内存里单词上百个就扛不住了。FragmentStateAdapter会自动销毁不可见的 Fragment这才是正确的选择。// 关键设置代码片段 ViewPager2 viewPager findViewById(R.id.viewPager); viewPager.setAdapter(new WordCardAdapter(this, wordList)); viewPager.setOffscreenPageLimit(1); // 只预加载左右各一页这个扩展可以单独做一个界面不必改动原有的列表逻辑。期末考试评分的重心通常是功能完整性和代码可读性界面炫酷只占一部分千万别把时间全耗在自定义动画上——数据库字段设计、Adapter 逻辑、生命周期处理才是高分区。6. 提交前最后 20 分钟验证功能、打包 APK、准备答辩话术代码改完先别急着生成 APK。按“新增功能 → 原有功能回归”的顺序把主流程走一遍。我自己的复验步骤是冷启动 App → 搜索一个单词 → 点进去看详情 → 收藏 → 杀掉进程重进确认收藏还在 → 添加一个自定义单词 → 重启确认写入成功 → 打卡按钮连续点两次确认不会重复计数。这套流程跑完不出问题再谈打包。打包用Build Generate Signed Bundle / APK。如果没有签名文件选Create new...填一个密码和别名即可。这里有个血泪教训签名文件的密码一定要记在项目 README 里或者至少写在本地笔记里不然两周后你改了代码想重新打包发现密码忘了得重新生成签名老用户就升级不了了。期末交作业阶段没这么复杂但你养成记录习惯总是好的。打包完 APK 后还要留一份“答辩预演”用的截图或录屏。安卓课设答辩时老师很少现场长时间操作通常让你展示功能点。提前把单词搜索、收藏、打卡、数据库变化这几个关键界面截图贴进答辩 PPT 或文档里比现场找入口快得多。建议录屏控制在 3 分钟内包含一次完整的“查词 → 收藏 → 重启确认 → 打卡”路径准确性比炫技重要。如果你还有余力把项目根目录的README.md写清楚数据库表结构、三个核心类的职责、如何导入和运行。这看起来不是代码功能但很多老师会在答辩时翻你的 README写得规范直接体现工程素养。我的习惯是每次提交作业前上滑看一遍自己的 README如果自己都懒得看那老师也不会看这份就白写了。希望这些经验能帮你的安卓期末大作业少走点弯路。本文还有配套的精品资源点击获取
返回列表