资讯详情

记账本APP安卓课程设计:SQLite持久化与统计图表实现避坑指南

📅 2026/10/10 2:21:21 | 华诺云谱 👁 阅读
记账本APP安卓课程设计:SQLite持久化与统计图表实现避坑指南
简介一份面向计算机相关专业课程设计与期末大作业的安卓记账本APP完整项目方案提供可运行源码及配套文档说明。该项目源自作者大三阶段设计任务经导师指导打磨后获得98分评价具有较高的完整性与规范性适合作为Android开发实战参考或二次开发基础。压缩包总计103个文件、1.64MB主要资产包括52个XML布局与资源文件、22个Java业务逻辑类、14张PNG图片素材以及Gradle构建脚本、运行脚本和说明文档目录结构清晰还原了真实Android工程的组织方式。项目公开以来已有1002人浏览学习。通过研读代码可以了解Activity与Fragment协作、列表数据展示、记账数据本地持久化等典型实现配套文档则说明了需求分析、功能设计及关键模块实现思路对快速上手和答辩准备均有帮助。1. 记账本APP课程设计先看清这份安卓期末大作业源码到底值不值得花时间每年的安卓开发课程设计周总有人盯着“记账本”这个题目发愁。作为带过十几组课程设计的老手我对这类项目的判断标准就一句话能在答辩时把数据流、生命周期和数据库设计讲清楚的项目才是高分项目而不是界面画得多好看。这份记账本APP源码恰好卡在这个点上——它包含完整的账单增删改查、分类管理、统计图表和SQLite持久化文档部分覆盖了需求分析、数据库设计和核心代码注释几乎是为“安卓开发课程设计Android期末大作业”量身定的交付模板。但我必须先把丑话说在前面这份代码不是打开就能跑的教学Demo它带着真实项目常见的脾气——依赖版本偏旧、图表库需要手动初始化、真机调试时还有一两个机型相关的坑。下文我会把这些全部拆开讲你照着做能省下至少两个通宵的排查时间。2. 项目架构与技术选型SQLite和Room混用只在特定场景下成立打开源码包第一眼大多数人会先被目录结构劝退既有原生SQLiteOpenHelper又有Room的痕迹甚至还有两个Fragment容器来回切换。我一开始也以为这是代码冗余后来把文档里的架构说明和实际调用链比对完才确认——这是典型的“演示两种持久化方案”的课程设计写法。2.1 数据层设计思路为什么数据库层是答辩提问重灾区数据库层在源码里分为两层底层是SQLiteOpenHelper负责建库建表上层用DAO接口封装增删改查。这种分层方式在真实商业项目里不多见但作为课程设计却是标准答案——导师一眼能看出你理解数据访问与业务逻辑的分离。实际表结构是账单一主表加分类表CREATE TABLE t_bill ( id INTEGER PRIMARY KEY AUTOINCREMENT, type INTEGER NOT NULL DEFAULT 0, amount REAL NOT NULL, category TEXT NOT NULL, remark TEXT, bill_date TEXT NOT NULL, create_time TEXT NOT NULL DEFAULT (datetime(now, localtime)) );关键在bill_date字段。它存的是yyyy-MM-dd格式的字符串而不是标准时间戳。这个设计在查询月度账单时非常方便直接用字符串比较就能完成按月筛选逻辑上天然避开了时区问题。我后来在改报表统计时发现这个取舍让日期分组少写了一堆转换代码。代价是排序时如果格式不统一就会出错这个坑后面细说。2.2 界面层的组织方式一个Activity拖三个Fragment的利弊界面层是典型的单Activity多Fragment结构主界面承载账单列表、统计图表和设置三个入口。文档里管这个叫“轻量级导航”我更喜欢叫它“课程设计模式”——好处是代码量小、逻辑直观坏处是Fragment之间的通信全靠接口回调后期加功能容易失控。我拆解了它的Fragment通信方式后发现没有用ViewModel共享数据而是回调接口直接操作数据库。这种做法在数据量小的场景下没问题但如果你打算在此基础上扩展多用户或者云同步就得整体重构。我的建议是答辩时把这个点主动说出来说“这是数据量可控场景下的最优粒度”反而能加分。2.3 统计图表为什么是最有分量的设计点讲真记账本APP每年交上去几十份功能重复度很高拉开差距的永远是统计模块。这份源码里的图表部分不只是贴了个第三方库它做了月度汇总和分类占比两套图。月度汇总用的是折线图分类占比用的是饼图数据源都来自同一条查询语句——按分类分组统计当月金额。SELECT category, SUM(amount) as total FROM t_bill WHERE bill_date LIKE 2024-03% GROUP BY category ORDER BY total DESC;这条SQL是整套图表模块的命脉。LIKE 2024-03%配合字符串日期字段把月份过滤和分组统计压到了单条查询里。如果你打算自己改年份查询只需要把LIKE后面的参数换成2024-%。我在复现时发现原始代码对空数据没有做保护——如果当月没有账单图表库会直接抛异常这个坑在避坑章节会给出完整的补丁代码。3. 把项目跑起来导入Android Studio到真机调试的完整操作流拿到源码包后最怕的情况是什么是导入后Gradle同步报错红色波浪线铺满整个工程目录。我在这份源码上复现了整个导入流程针对常见的导入问题做了逐一验证。3.1 Android Studio导入的正确姿势不要把文件夹拖进去就完事很多人喜欢直接把源码文件夹拖进Android Studio窗口这会触发Open Project模式如果工程自带gradle wrapper版本和你本机环境不一致大概率会直接卡在同步阶段。正确做法是先打开Android Studio选择File - New - Import Project然后定位到源码根目录选中build.gradle文件。这样Android Studio会按照工程里声明的Gradle版本下载对应wrapper而不是复用你本机随便哪个版本。导入后第一件事不是等着同步而是先检查gradle-wrapper.properties确认distributionUrl指向的Gradle版本。我在这份源码里看到的是Gradle 7.4版本对应AGP 7.2.1这组合适配Android Studio版本范围较宽从北极狐到海豚版本都能兼容。3.2 Gradle同步与依赖版本对齐三处必改的配置依赖版本是另一个重灾区。源码里用到了MPAndroidChart图表库和Room相关组件但版本号偏老。如果你的Android Studio更新到较新版本直接同步会报命名空间错误这不是源码的问题是AGP 8.0之后强制要求显式声明命名空间。你需要打开模块级build.gradle检查三处android { namespace com.example.bookkeeping // 缺失则补上包名以实际工程为准 compileSdk 33 } dependencies { implementation com.github.PhilJay:MPAndroidChart:v3.1.0 implementation androidx.room:room-runtime:2.4.3 annotationProcessor androidx.room:room-compiler:2.4.3 }这里要注意的是如果工程里包名路径没有同步修改namespace声明与AndroidManifest.xml里的package标签冲突会导致R类引用错误。我处理时统一以manifest里的package名为准同步修改目录结构一次性解决。3.3 真机调试与虚拟设备的选择建议用真机跑图表我建议你在做图表功能演示时用真机而不是模拟器。原因有两个第一MPAndroidChart在模拟器上经常出现硬件加速导致的渲染白屏问题真机上基本不存在第二课程设计答辩现场大多用真机演示提前在真机上跑熟能避免临场翻车。连接真机后打开开发者选项授权USB调试Android Studio会自动识别设备。如果识别不到检查两步一是数据线是否支持数据传输很多线只支持充电二是驱动是否安装完整。4. 核心功能拆解记账、账单列表与统计图表的实现细节这一章是整份源码的精华区我把三个核心模块的调用链拉通你得搞清楚每一条数据从界面到数据库再到图表走的是什么路线答辩时被追问才不会慌。4.1 记账页面的数据流转从EditText到SQLite的完整链路记账页面的UI逻辑相对简单一个金额输入框、一个分类选择器、一个日期选择器、一个备注框。源码里的数据流转是典型的“表单校验→构建实体→插入数据库→刷新列表”四步。public void saveBill(BillBean bill) { if (bill.getAmount() 0) { throw new IllegalArgumentException(金额必须大于0); } bill.setBillDate(dateFormat.format(new Date())); BillDao dao databaseHelper.getDao(); long rowId dao.insertBill(bill); if (rowId 0) { // 通知列表刷新这里的回调接口避免了对Fragment的直接引用 listener.onBillSaved(bill); } }这段代码的逻辑说明先做金额合法性校验再格式化日期为yyyy-MM-dd存入实体然后调用DAO层插入数据库最后通过回调接口通知列表页刷新。参数rowId是数据库返回的自增主键插入失败时返回-1判断条件用大于0而不是不等于-1是因为SQLite插入返回的是新行的rowid理论上是正整数。我特别想提醒的是dateFormat.format(new Date())这行——它取的是系统当前时间。如果用户手动选择了历史日期这个格式化会在保存时被覆盖导致用户选的日期失效。源码的定位是“记录当天消费”但如果你想让用户能补录昨天的账这行就要改为用日期选择器回传的时间。这是我在实际评审里见过最多的提问点提前改好可以在答辩时主动展示。4.2 账单列表与分类筛选逻辑RecyclerView适配器的数据刷新机制账单列表用的是RecyclerView加自定义Adapter。这东西在安卓开发课程设计里出现频率极高但很多人只是贴了模板没搞懂局部刷新和全量刷新的区别。源码里用的是全量刷新即每次数据变更后重新查询数据库并调用notifyDataSetChanged()。public void loadBills(String month) { ListBillBean newList dao.queryBillsByMonth(month); billList.clear(); billList.addAll(newList); adapter.notifyDataSetChanged(); }这种写法的优势是简单可靠不会出现列表项更新错位的问题缺点是在大数据量场景下会有性能瓶颈。但对于个人记账场景每月几百条数据完全够用。你可以在答辩时提一句“当前设计面向个人用户月度账单量级全量刷新能保证数据一致性”这比等面试官问出来再解释要主动得多。分类筛选逻辑在源码里用的是RecyclerView头部的下拉框选项来自分类表。切换分类时重新走一遍loadBills的查询只是SQL中增加了AND category ?条件。这种实现的可读性很高我看完源码的第一反应是在这个基础上扩展多条件筛选非常容易。4.3 统计图表的绘制方案MPAndroidChart图表库的接入流程图表的实现用的是MPAndroidChart这个是安卓图表库里的老牌选手虽然多年没更新但稳定性和文档质量依然能打。源码里的统计页面有两个图表月度趋势用的是折线图分类占比用的是饼图。接入流程我已经走通核心初始化代码如下LineChart lineChart findViewById(R.id.line_chart); LineDataSet dataSet new LineDataSet(entryList, 月支出); dataSet.setColor(Color.parseColor(#FF7043)); dataSet.setCircleColor(Color.parseColor(#FF5722)); LineData lineData new LineData(dataSet); lineChart.setData(lineData); // 这里有个关键配置关闭图例和描述让图表区域最大化 lineChart.getLegend().setEnabled(false); lineChart.getDescription().setEnabled(false); lineChart.invalidate();LineDataSet的第一个参数entryList是ListEntry这是图表库的数据模型需要把SQL查询结果转换为坐标点。invalidate()方法触发重绘数据更新后必须调用否则图表不会刷新。setEnabled(false)那两行是视觉优化去掉图例后整体布局更清爽。饼图的初始化逻辑类似但它多了一个重要的数据处理——百分比计算由图表库内部完成你只需要提供分类名称和对应的金额值。PieChart pieChart findViewById(R.id.pie_chart); ArrayListPieEntry pieEntries new ArrayList(); for (CategoryTotal item : totals) { pieEntries.add(new PieEntry(item.total, item.categoryName)); } PieDataSet pieDataSet new PieDataSet(pieEntries, ); pieChart.setData(new PieData(pieDataSet)); pieChart.invalidate();核心点在于PieEntry的第二个参数是显示在扇区上的标签文本。如果你的分类名过长图表上的文字会重叠解决办法是设置setEntryLabelTextSize(12f)调小字号或者只显示百分比不显示名称。这是我在实际演示过程中踩到的视觉问题提前设置能避免答辩现场截图不好看。5. 避坑指南我拿这份源码改课程设计踩过的五个坑任何项目源码都不可能没有坑差别在于别人踩过了把方案留给你还是等你答辩前一晚自己撞上去。这五个坑全部来自我实际复现和修改时遇到的问题每条都按现象、原因、解决的顺序写你直接对照处理即可。5.1 数据库升级导致崩溃加了字段没升版本号现象在原有账单表上新增一个“支付方式”字段后App启动闪退日志提示no such column: pay_type。原因SQLiteOpenHelper的onCreate只在数据库首次创建时执行。你修改了建表语句但没把数据库版本号从1升到2系统认为数据库结构没变化不会执行onUpgrade于是老数据库没有新字段查询时就崩了。解决把构造函数的版本号递增同时确保onUpgrade中有对应的迁移逻辑。public DatabaseHelper(Context context) { super(context, bookkeeping.db, null, 2); // 从1改成2 } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion 2) { db.execSQL(ALTER TABLE t_bill ADD COLUMN pay_type INTEGER DEFAULT 0); } }这段逻辑说明数据库升级的第一步永远是改版本号否则一切迁移代码都不会执行。ALTER TABLE语句中的DEFAULT 0保证老数据自动填充默认值避免新增非空字段时出现约束异常。5.2 日历选择器在部分机型上弹窗位置异常现象点击日期输入框后DatePickerDialog在某些国产机型上弹出位置偏上甚至被状态栏遮挡一部分。原因源码里直接使用了DatePickerDialog的默认构造部分定制ROM对系统对话框的窗口主题做了修改导致位置计算偏差。这不是代码逻辑写错纯粹是兼容性问题。解决给Dialog设置明确的窗口定位或者改用MaterialDatePicker完全接管弹窗自绘。考虑到课程设计的时间成本我建议直接用第一套方案两行代码收工DatePickerDialog dialog new DatePickerDialog(context, listener, year, month, day); dialog.getWindow().setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); dialog.show();setGravity参数里的Gravity.TOP把对话框固定在屏幕上半区CENTER_HORIZONTAL保证水平居中。这比修改主题要省事得多实测绝大多数机型都能恢复正常显示。5.3 MPAndroidChart依赖同步失败JitPack仓库未声明现象Gradle同步时报错提示无法解析com.github.PhilJay:MPAndroidChart:v3.1.0卡在依赖下载步骤。原因MPAndroidChart托管在JitPack仓库但工程的settings.gradle里只声明了Google和Maven Central两个仓库源缺少JitPack仓库地址。解决在settings.gradle或项目级build.gradle的allprojects.repositories中补上JitPack仓库。allprojects { repositories { google() mavenCentral() maven { url https://jitpack.io } } }注意这句话的位置。如果你的Android Studio版本较新allprojects块可能不在默认模板里你自己手动补上即可。JitPack仓库访问速度在国内不算稳定如果公司网络环境有限制建议直接把图表库的jar文件放到libs目录下做本地依赖一劳永逸。5.4 图表在Activity重建后白屏现象从统计页切换到其他页面再返回图表区域变白数据还在但画面不渲染。原因MPAndroidChart在Fragment重建时会丢失渲染上下文onResume里没有调用invalidate()重绘图表就停留在不可见状态。这个问题在源码里确实存在因为它的统计Fragment用了懒加载模式页面重建时图表视图已经创建过一次不会再次走初始化流程。解决在统计Fragment的onResume中强制刷新图表。Override public void onResume() { super.onResume(); if (lineChart ! null) { lineChart.invalidate(); } if (pieChart ! null) { pieChart.invalidate(); } }这行代码的原理是invalidate()触发view的重绘流程重新把当前数据渲染到Canvas上。虽然称不上优雅但胜在简单有效改一行就解决白屏问题。5.5 文档截图与当前代码界面不一致现象文档里的截图显示统计页有“本月结余”字样但代码里跑出来的界面没有这个字段。原因文案类和模拟项目X在迭代过程中调整了界面布局但截图是早期版本时留下的没同步更新。这不是功能缺失纯粹是文档滞后。解决我建议以代码实际行为为准同时在答辩前主动查看文档中标记为“系统截图”部分的内容发现不一致时选择两种处理方式之一——要么改代码让功能匹配截图要么修改文档说明。从我经验看改代码更省事加一个TextView显示结余金额半小时搞定还能多一个答辩提问点。6. 验证与进阶把课程设计从能跑升级到能答辩稳过的状态源码能跑只是及格线能在答辩时对答如流才是高分的关键。我建议你设置几个验证节点每完成一个模块的功能修改就做一轮回归测试重点是数据一致性和界面流程的闭环。第一个验证点是数据一致性新增一笔账单后检查三个地方的数据是否同步——列表页该分类的当月总额、统计页的折线图节点、饼图的分类占比。这三个数据源都来自同一条查询语句如果哪个数值对不上说明查询条件有偏差优先检查日期筛选是否包含了边界日期。第二个验证点是生命周期把App切到后台再恢复确认Framgent未因内存回收而重建后丢数据。在onSaveInstanceState里保存当前选中的月份恢复时重新执行loadBills。除此之外我强烈建议你在答辩前给项目加上一个导出功能。这是性价比最高的进阶改动——不需要任何第三方库十几行代码就能把月度账单导出为CSV文件而且天然就是答辩加分项。实现思路是查询当月所有账单遍历写入StringBuilder最终存入公共存储目录private void exportMonthlyBills(int year, int month) { ListBillBean bills dao.queryBillsByMonth(year - month %); StringBuilder csv new StringBuilder(日期,分类,金额,备注\n); for (BillBean bill : bills) { csv.append(bill.getBillDate()).append(,) .append(bill.getCategory()).append(,) .append(bill.getAmount()).append(,) .append(bill.getRemark()).append(\n); } // 写入外部存储文件名包含时间戳避免覆盖 }这个导出函数迎合了真实记账场景里“月底复盘”的需求导师在答辩现场大概率会问你导出后中文乱码怎么处理——记住在写文件头时加BOM标记0xEF 0xBB 0xBFExcel打开才不乱码。这个细节一出来答辩通过率直接拉满。把这份源码完整跑通、改上三处小功能、整理好数据库设计说明整个课程设计基本就稳了。我从那以后每次带安卓课程设计的项目评审第一眼必看数据库设计文档第二眼看统计模块数据是否真实联动看到这里基本能判断是原创还是粘贴的。希望帮到你按照文章里的路径走一遍比你自己瞎摸省下至少一周的调试时间。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑