news 2026/10/2 3:22:51

Android记账本毕设全攻略:从SQLite到RecyclerView实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android记账本毕设全攻略:从SQLite到RecyclerView实战

如果你正在准备Android方向的毕业设计,想在几个月内拿下一个既写得出深度、又能经得住答辩追问、还可以直接拿到源码参考完整方案的题目,记账本这个方向我建议你认真考虑。我自己当年就是靠一个记账本App拿下的优秀毕设,后来工作里也带过不少学弟学妹做类似题目,对这一整套流程非常熟。这篇文章不打算把代码从头贴到尾,而是把记账本项目从立项、技术选型、数据库设计、功能拆解、源码阅读到答辩准备的完整链路都拆开讲一遍,手把手告诉你每一步该怎么做、为什么这么做,以及哪些环节容易踩坑。不管你手里拿到的是哪一份源码,看完这篇你都能把它变成自己的东西。

1.2 技术选型:原生Android、Java还是Kotlin

很多同学第一步就卡在语言选择上。我的建议很直接:如果你的目标是顺利毕业且手中已有可参考的源码,优先选Java。原因不是Kotlin不好,而是市面上的毕设参考资料、老版本源码、答辩时导师可能理解的代码习惯,Java版本的存量最大。你拿到一份Java写的记账本源码,改起来、查问题、问别人,成本都最低。

如果是纯从零开始且时间充裕,Kotlin当然也没问题,但要注意:Kotlin的扩展函数、协程、空安全机制虽然写起来舒服,少数老派导师的提问角度反而会更多。我见过不止一个用Kotlin写毕设的同学,在答辩时被追着问“这个语法和Java有什么区别”“为什么不用Java写”,虽然这不是什么难题,但要多准备一层说辞。

数据库方面,记账本这个体量的项目用Android内置的SQLite完全足够。对比一下三种常见方案:

方案优点缺点适合场景
原生SQLite无需额外依赖、SQL直观可控、源码易懂需要自己写SQL和类型转换毕设、教学项目、轻量应用
RoomGoogle官方ORM、支持LiveData与Flow、编译期校验SQL学习成本高一层、注解概念多商用项目、架构课设、加分项
GreenDAO性能好、API简洁需要生成代码、维护成本高老项目维护、大数据量

毕设里我推荐原生SQLite,理由很实在:答辩老师最关心的是你有没有掌握数据存储的基本功。你能现场写出一条INSERT、讲清楚数据库升级和事务处理,比单纯说“我调用了Room的DAO接口”更能拿分。SQLite方案的SQL是透明的,你控制的是每一个字节的存储逻辑,这反而成了你答辩时最扎实的论据。

1.3 项目结构规划与包名设计

记账本再小,我仍然建议你在动手前把项目结构规划好。不要所有类都堆在一个包下面,那样后续改起来会非常难受。一个合理的结构可以这样分:

com.example.accountbook ├── activity // MainActivity、LoginActivity、RegisterActivity ├── adapter // RecyclerView的Adapter、ViewPager的Adapter ├── bean / entity // 账目Bean、分类Bean、用户Bean ├── db // SQLiteOpenHelper实现、DAO层 ├── fragment // 记账Fragment、统计Fragment、设置Fragment ├── utils // 日期工具、金额工具、SP工具 └── view // 自定义View:数字键盘、进度条等

包名是个容易被忽略的细节。很多初学同学直接默认com.example.myapplication,这个在答辩时会被一眼看“糙”。你可以起一个稍微像样点的包名,比如com.yourname.accountbook,虽然只是命名习惯,但给老师的印象完全不同——专业的包名直接传达了你的工程素养。

分包之后,Activity只负责界面跳转和数据展示,Fragment负责单屏业务逻辑,DAO层负责所有数据库操作,Bean类只承载字段。这种分层是毕设拿高分的基本盘,也是后续源码阅读与二次开发的骨架。

2. 数据库设计与架构分层

数据库是记账本项目的灵魂。你要记一笔账、查一份报表、算一个分类占比,底层全靠表结构和SQL语句撑起来。很多同学在这一步糊弄过去,导致后面功能越写越别扭。

2.1 SQLite建表逻辑与SQL语句

记账本核心表至少要有三张:用户表、账目表、分类表。如果做预算提醒,再加一张预算表。先看最简单的用户表:

CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL, create_time TEXT NOT NULL );

然后是账目表,这是整个项目的核心表,字段要仔细设计:

CREATE TABLE bill ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, type INTEGER NOT NULL, -- 1收入 2支出 category_id INTEGER NOT NULL, -- 关联分类表 amount REAL NOT NULL, -- 金额(建议以“分”为单位存储为INTEGER) date TEXT NOT NULL, -- 日期 YYYY-MM-DD remark TEXT, -- 备注 create_time TEXT NOT NULL -- 创建时间 );

这里有一个非常实用的小技巧:金额字段不要用REAL存元,而是用INTEGER存分。原因很简单,浮点数在计算机内本身有精度问题,0.1加0.2你可能得到0.30000000000000004。虽然记账本里一般也就做展示,但你在统计报表、月度汇总时一旦出现小数位错乱,排查起来非常痛苦。用“分”存储,显示时除以100转成元,金额计算全部落在整数上,稳得一批。

分类表可以根据需求做成固定写死或动态读写。毕设阶段建议动态读写,这样你可以在设置页里支持用户添加自定义分类,功能上立刻多一个亮点:

CREATE TABLE category ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, name TEXT NOT NULL, icon TEXT NOT NULL, -- 图标标识,可用emoji或图片资源名 is_income INTEGER DEFAULT 0 -- 是否为收入分类 );

2.2 DAO层封装与全局单例

很多初学同学习惯在Activity里直接写db.execSQL(),这样写一两个页面还行,功能一多就乱了。我建议把数据库操作集中封装到一个DAO类里,比如BillDao.java,提供增删改查方法:

public class BillDao { private SQLiteDatabase db; private static BillDao instance; private BillDao() { db = DBHelper.getInstance(context).getWritableDatabase(); } public static BillDao getInstance() { if (instance == null) { instance = new BillDao(); } return instance; } public long insertBill(BillBean bill) { ContentValues values = new ContentValues(); values.put("user_id", bill.getUserId()); values.put("type", bill.getType()); values.put("category_id", bill.getCategoryId()); values.put("amount", bill.getAmount()); values.put("date", bill.getDate()); values.put("remark", bill.getRemark()); values.put("create_time", bill.getCreateTime()); return db.insert("bill", null, values); } public List<BillBean> queryBillsByDate(String date) { List<BillBean> list = new ArrayList<>(); Cursor cursor = db.query("bill", null, "date=?", new String[]{date}, null, null, "create_time DESC"); while (cursor.moveToNext()) { BillBean bean = new BillBean(); bean.setId(cursor.getLong(cursor.getColumnIndex("id"))); bean.setUserId(cursor.getLong(cursor.getColumnIndex("user_id"))); bean.setType(cursor.getInt(cursor.getColumnIndex("type"))); bean.setAmount(cursor.getLong(cursor.getColumnIndex("amount"))); // 单位为分 bean.setDate(cursor.getString(cursor.getColumnIndex("date"))); list.add(bean); } cursor.close(); return list; } }

单例模式的目的是让全局只有一个数据库访问入口,避免重复连接和资源浪费。要注意的是,DBHelper本身也应该是单例,否则多次调用getWritableDatabase()可能会产生多个实例。

2.3 MVC分层在Android里的落地方式

MVC在Android里的体现其实很直观:XML布局就是View,Activity/Fragment就是Controller,DAO和Bean就是Model。你要在答辩时把这个说清楚。

我见过不少同学把几十行业务逻辑堆在Activity里,美名其曰“快速开发”,实际上非常不利于后面扩展。比如你想做一个“按月份统计支出总额”的功能,正确做法是在BillDao里写一个方法:

public double getMonthTotalExpense(long userId, String month) { String sql = "SELECT SUM(amount) FROM bill WHERE user_id=? AND type=2 AND date LIKE ?"; Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(userId), month + "%"}); double total = 0; if (cursor.moveToFirst()) { total = cursor.getLong(0); } cursor.close(); return total; }

然后Activity里只需要调用方法展示结果。如果你把这条SQL写在Activity里,下次统计功能一变,你就得把Activity翻个底朝天。分层的意义就是:数据库的东西留在数据库层,界面的东西留在界面层,谁也不越界。

3. 核心功能模块的逐个实现

记账本的功能模块看起来不复杂,真正动手做才发现每个模块都有值得打磨的细节。这一部分我按用户的使用路径来拆解。

3.1 记账主流程:数字键盘与金额输入

记账页是整个App的脸面,用户一打开就要看到顺手的手势和高效的操作逻辑。金额输入方面,我强烈建议你做一个自定义键盘而不是直接用系统键盘。好处是:

  • 用户不用在系统键盘和界面之间来回切换,操作路径短
  • 可以限制只能输入数字和小数点
  • 排版统一,视觉效果更专业

自定义键盘的实现思路不复杂,一个PopupWindow或者Fragment底部弹出的一个View,里面放数字按钮0-9、小数点、删除键和完成键,按钮点击后把内容追加到TextView上。我建议用GridLayout写九宫格的数字键盘,代码量不大,但布局感觉一下就出来了。

金额校验也是必须处理的细节。你不能让用户输入1.2.3或者空金额,也不能让用户输入超过两位小数。这里的校验逻辑很简单:

private String getValidInput(String current, String append) { if (current.contains(".")) { // 已经包含小数点则不允许再输入小数点 if (".".equals(append) || current.split("\\.")[1].length() >= 2) { return current; } } // 首位为0时如果再输入数字,直接替换 if (current.equals("0") && !".".equals(append)) { return append; } return current + append; }

3.2 分类管理与九宫格图标布局

记账功能里总要选一个分类:吃饭、交通、购物、工资、理财……分类页的展示方式最常用的是GridView九宫格,这个也是我在热搜词里看到“android九宫格”的原因——确实是高频需要。

分类图标推荐直接用emoji或矢量图。用emoji最简单,一个字符串就能表示一个分类:🍜代表餐饮、🚌代表交通、🛒代表购物、💼代表工资。不用额外拉一套图标资源,也避免了图片适配问题。在GridView的Adapter里,你只需要把emoji字符串绑定到TextView上:

public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(context) .inflate(R.layout.item_category, parent, false); } TextView tvIcon = convertView.findViewById(R.id.tv_icon); TextView tvName = convertView.findViewById(R.id.tv_name); CategoryBean bean = list.get(position); tvIcon.setText(bean.getIcon()); tvName.setText(bean.getName()); return convertView; }

注意一个交互细节:用户选中分类后要把高亮状态做出来,否则用户不清楚当前选中的是哪个。你可以用一个selectedPosition记录选中的位置,在Adapter里根据位置用不同的背景色或透明度区分。

3.3 数据统计:MPAndroidChart折线图与饼图

统计页是记账本区别于普通CRUD小程序的核心亮点,也是你答辩时最值得展开讲的部分。一个月饼图展示支出分类占比,一个折线图展示每日支出趋势,这两张图一出来,整个项目的档次就上去了。

图表库方面,业界最常用的是MPAndroidChart,在GitHub上星星数超过一万,文档齐全、中文资料多。添加依赖:

implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'

这张折线图的配置比较绕,我第一次用的时候也试错了几次:

LineChart lineChart = findViewById(R.id.chart_line); List<Entry> entries = new ArrayList<>(); for (int i = 0; i < dateList.size(); i++) { entries.add(new Entry(i, (float) dailyTotalList.get(i))); } LineDataSet dataSet = new LineDataSet(entries, "每日支出"); dataSet.setColor(Color.parseColor("#FF6B6B")); dataSet.setLineWidth(3f); dataSet.setCircleRadius(5f); dataSet.setValueTextSize(14f); LineData lineData = new LineData(dataSet); lineChart.setData(lineData); lineChart.getDescription().setEnabled(false); lineChart.setTouchEnabled(true); lineChart.setPinchZoom(true); lineChart.animateX(1500);

这里有一个关键技巧:X轴显示日期要设置成字符串,而不是直接用索引数字。MPAndroidChart原生支持ValueFormatter,你需要新增一个类实现IndexAxisValueFormatter,把索引转换成“周一、周二”或者“3/5、3/6”这种显示。否则图表的可读性会大打折扣。

饼图用于分类占比:

PieChart pieChart = findViewById(R.id.chart_pie); List<PieEntry> entries = new ArrayList<>(); for (CategoryBean bean : categoryList) { entries.add(new PieEntry((float) bean.getMoney(), bean.getName())); } PieDataSet dataSet = new PieDataSet(entries, "分类支出"); dataSet.setColors(ColorTemplate.VORDIPLOM_COLORS); dataSet.setValueTextSize(14f); PieData pieData = new PieData(dataSet); pieChart.setData(pieData); pieChart.setUsePercentValues(true); pieChart.getDescription().setEnabled(false); pieChart.animateY(1000);

饼图不要忘了setUsePercentValues(true),这是显示百分比的开关。颜色方面可以用ColorTemplate.VORDIPLOM_COLORS或者自己配一组色值,让色块之间有明显区分。

3.4 首页列表与RecyclerView的刷新逻辑

首页账单列表按日期分组展示,是用户最常看的界面。按时间倒序排列最近的支出记录,每条记录显示图标、分类名、备注、金额。这里我用RecyclerView,配合一个自定义的Adapter。

列表数据加载后,有一个常见的坑:从数据库里查询完数据后忘记在UI线程通知Adapter刷新。比如你从子线程执行查询,然后直接改内存里的List,界面没反应,你怎么找都找不到问题。正确的做法是:

new Thread(() -> { List<BillBean> list = billDao.queryAllBills(); runOnUiThread(() -> { adapter.setList(list); adapter.notifyDataSetChanged(); }); }).start();

或者如果你用了Android的AsyncTask、Handler等机制,也都记得最终要回到主线程再更新UI。

列表还应该支持左滑删除或者长按删除。用ItemTouchHelper可以快速实现左滑删除效果,这个功能不算必须,但加上以后操作体验会很完整。如果为了控制代码量,一个长按弹窗确认删除也完全够用。

4. UI细节与交互优化

一个记账本能不能让人爱用,很多时候不是功能多少,而是细节舒不舒服。这一部分我挑几个毕设里做得出来又不难的UI点展开。

4.1 CoordinatorLayout与Banner的联动效果

热搜词里多次出现“协调布局+banner”,我猜测很多同学是想把首页做出一滑就折叠的效果。这个在记账本里很实用:顶部放个统计卡片,往下滑动详情列表时,顶部卡片跟着收起,这个效果用Android自带的CoordinatorLayout + AppBarLayout就能实现。

<androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <com.google.android.material.appbar.CollapsingToolbarLayout android:layout_width="match_parent" android:layout_height="wrap_content" app:title="记账本" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <LinearLayout android:layout_width="match_parent" android:layout_height="200dp" android:orientation="vertical" android:background="@color/colorPrimary" app:layout_collapseMode="parallax"> <!-- 这里放余额总览卡片 --> </LinearLayout> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.recyclerview.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>

关键点在两个:一个是app:layout_scrollFlags里要加上scroll和exitUntilCollapsed,否则滚动时顶部卡片不会收起;另一个是RecyclerView所在的LinearLayout要加app:layout_behavior="@string/appbar_scrolling_view_behavior",这样才能把滚动事件联动到AppBar上。这两个属性缺一个,整个折叠效果就废了。

当时我调试这个布局的时候,遇到过滚动时列表把顶部卡片直接顶走的情况,最后发现就是少了behavior属性。你如果踩到一样的坑,先检查这两个地方。

4.2 首页五宫格与快捷入口

如果你希望首页功能更丰富一些,可以在列表上面再加一行快捷入口:当月支出、当月收入、预算剩余、分类排行、导出账单。每次点击跳转不同页面。这五个入口用一个横向滑动的RecyclerView或者简单的LinearLayout实现都可以。

我建议做一个“当月支出”和“当月收入”的统计卡片,在进入首页时自动查询数据库并填充,这个就是比较典型的MVP式需求了——界面展示、数据库查询、数据刷新三个动作的节奏配合,做一遍之后你对整个项目的数据流就全懂了。

4.3 权限申请与账单导出

账单导出是个非常好的加分功能,用了很多同学都不做的亮点。把这个做成“导出CSV文件,分享给微信/钉钉”,你要先处理Android运行时权限问题——Android 6.0及以上,写入外部存储需要在运行时请求,仅AndroidManifest.xml里声明是不够的。

if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); }

然后导出逻辑用CSV格式,逗号分隔,Excel可以直接打开:

private void exportBillsToCsv(List<BillBean> list) { StringBuilder sb = new StringBuilder(); sb.append("日期,类型,分类,金额,备注\n"); for (BillBean bill : list) { sb.append(bill.getDate()).append(",") .append(bill.getType() == 1 ? "收入" : "支出").append(",") .append(bill.getCategoryName()).append(",") .append(bill.getAmount()).append(",") .append(bill.getRemark()).append("\n"); } File file = new File(getExternalFilesDir(null), "account_export.csv"); try { FileOutputStream fos = new FileOutputStream(file); fos.write(sb.toString().getBytes()); fos.close(); Toast.makeText(this, "导出成功:" + file.getAbsolutePath(), Toast.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); } }

这里用getExternalFilesDir(null)作为导出目录很关键。Android 10分区存储后,直接写/storage/emulated/0/路径经常被拒绝,但getExternalFilesDir()路径下的文件自己可以随意读写,而且不需要额外的存储权限。这个方案既避开了繁琐的SAF文件授权流程,又保证功能可以正常演示。

5. 源码阅读方法与二次开发规划

你用“毕设附源码79383”这类关键词拿到一份现成项目后,最大的问题不是“源码能不能跑”,而是“这源码你怎么看懂、怎么改成自己的东西”。这一部分我来讲实用的源码阅读方法。

5.1 拿到源码后先做这三件事

第一件事,看README。虽然很多毕设源码的README写得敷衍,甚至没有,但只要有就要先看,它至少能告诉你项目运行的最低要求。

第二件事,看AndroidManifest.xml。这个文件是整个App入口的全景图:有哪些Activity、哪些权限、哪个是启动页(MAIN/LAUNCHER intent-filter),一看就明白App的页面结构。我一般用这个文件来快速建立项目的“地图”,比直接去读几十个Java文件高效得多。

第三件事,看核心Bean类和DAO层。打开项目里的bean目录,看看BillBean有哪些字段,再打开db目录看看数据库表结构和SQL语句,你就基本了解这个项目的存储构建了。这三步走完,项目在你心里已经有了骨架。

5.2 按用户主路径跟踪代码

不要试图一个文件一个文件地读下去,那样效率太低。要跟着用户的操作路径去看:

启动App -> 登录/注册页 -> 调用UserDao -> HomeActivity -> 记账按钮 -> 记账Fragment -> 保存按钮 -> 调用BillDao -> 刷新列表 -> 统计页 -> 调用统计方法。

这条路径走完,你差不多就清楚八成代码的作用了。我习惯在IDE里用“查找引用”功能(Ctrl+鼠标点击)跟踪方法的调用链,看到底是谁调用了谁,整个调用关系很快就浮现出来了。

这里提醒一句:源码的包名里可能有原作者的信息,你去做毕设前务必先改包名和项目名,改成自己的结构。这不只是面子问题,也是学术规范的基本要求。

5.3 增加亮点功能的三个方向

你手里有一份能跑的源码,如果只照着演示,那答辩的分很有限。我建议在源码基础上选择两三个亮点功能做增量开发,这是拉开差距的正确方式。方向可以这样选:

  • 预算提醒:在数据库加预算表,每月设置支出上限,超额时弹通知或者变色警告。这个功能逻辑不复杂,SQL也是现成的模式,但非常贴切“记账本”的实用定位。
  • 多账本切换:支持“日常账本”“旅行账本”“生意账本”,每次切换用户的账单列表和统计随之变化。这个只需要在bill表里加一个账本ID字段就行,改造成本很低。
  • 图表增强:折线图本来就是亮点,你再增加一个“月度对比柱状图”,展示最近六个月收入支出的趋势对比,直观又好看,答辩时视觉效果直接拉满。

6. 实测中踩过的坑与解决实录

这一部分是我真正想重点写的。几乎每个记账本毕设项目里,下面几个问题你迟早会遇到,而且网上有的方案说得云里雾里,我这里直接给你可复现的解决过程。

6.1 RecyclerView数据刷新不了

现象:记账成功后,列表还是旧数据,怎么滑都不变。

原因一般有两个:一是你在子线程里改了list,但没在主线程通知Adapter;二是你用了list = newList而不是list.clear(); list.addAll(newList),导致Adapter持有的引用还是原来的空list。这个坑太经典了,很多同学会在这个上面花费一晚上。

解决:写一个refreshData(List<BillBean> newList)方法,在Adapter内部明确执行:

public void refreshData(List<BillBean> newList) { mList.clear(); mList.addAll(newList); notifyDataSetChanged(); }

每次都调用这个方法,不用纠结引用问题。

6.2 SQLite查询结果没按预期排序

现象:查询7月的账目,结果把8月的也带出来了,或者日期顺序乱。

这个基本都是SQL语句的问题。比如你用date LIKE '%7%'去找包含“7”的日期,那7月、17号、27号全都会出现。正确的做法是用date LIKE '2024-07%'精确前缀匹配,或者用strftime('%Y-%m', date) = '2024-07'。排序则要明确加ORDER BY,SQLite不会主动根据主键给你排,你不写它就是插入顺序,按时间显示就乱了:

SELECT * FROM bill WHERE user_id=? AND strftime('%Y-%m', date)=? ORDER BY date DESC, create_time DESC;

6.3 金额显示出现多个小数点

现象:输入金额时不小心输入了1.2.3,虽然保存时没报错,但显示汇总数据时虽然格式化时保留了两位小数,但还是有奇怪的数据。

这个问题出在输入校验没做好。你看我前面3.1节给的输入校验方法,它就是从源头拦截非法字符的。如果你接手源码里没有校验函数,建议顺手补上。开源项目可以糙一点,但你的毕设代码里出现“金额有多位小数点还能保存”这种毛病,答辩时被问到会很尴尬。

6.4 中文乱码与输入法弹出问题

现象:安装后标题栏中文显示正常,但Toast或页面部分中文显示问号。

这类问题在毕设源码里不少见,通常是IDE默认编码没设置成UTF-8。在Android Studio里打开设置,搜索“File Encodings”,把Global Encoding和Project Encoding都改成UTF-8,再重新构建项目。我建议把IDE的默认编码一次调好,而不是等乱码出来再逐文件处理。

输入法弹出挤掉布局也是一个高频问题。在AndroidManifest.xml的Activity里加一行:

<activity android:name=".activity.AddBillActivity" android:windowSoftInputMode="adjustResize|stateHidden" />

这个设置让键盘弹出时布局自动压缩而不是顶掉整个页面,细节体验会好很多。

6.5 常见问题速查表

问题现象排查方向
列表不刷新新增/删除后界面无变化Adapter是否调用了notifyDataSetChanged,list引用是否被替换
数据库插入失败保存后查询无记录类型是否匹配,日期字段格式是否统一
图表空白折线图/饼图无数据展示Entry的x值是否从0连续递增,数据集颜色是否设置
崩溃:tablayout找不到fragment切页闪退Fragment实例是否通过getItem重新创建,底部导航的fragment状态必须保存
存储不足导出文件失败是否用getExternalFilesDir,目录是否为空

7. 答辩准备与展示路径

代码写完了、功能跑通了,很多同学以为万事大吉,实际答辩却是不少项目的“生死时刻”。程序写得不错但讲不出来、被老师的一句话问住而翻车的案例每年都有很多。这一部分我把自己的答辩经验分享给你,照着准备可以节省大量时间。

7.1 答辩PPT的讲述主线

PPT不用多,10到15页足够。讲述主线我建议按这个顺序:

  1. 选题背景与意义(为什么做记账本,解决什么痛点)
  2. 技术选型与可行性分析(用什么语言、数据库、图表库,为什么选)
  3. 系统功能结构(整个记账本有哪些功能模块,画一张功能图)
  4. 核心流程图(用户记账的流程:打开应用 -> 选择分类 -> 输入金额 -> 保存 -> 刷新列表)
  5. 数据库设计(三张核心表的结构与关系,重点展示bill表)
  6. 核心功能演示(现场操作,一步一步走)

最后一页放代码的核心片段,不用多,放两三个亮点就行:比如金额以“分”存储的处理逻辑、折线图的数据组装逻辑、CoordinatorLayout的联动效果。重点是讲出代码背后的思考过程。

7.2 演示脚本要提前演练

演示环节一定要提前演练,千万不要现场临场发挥。我在线下辅导学弟学妹时经常发现,他们演示的时候顺序混乱:一会儿点统计页,一会儿又点设置页,老师根本跟不上节奏。

我建议你设计一条固定的演示脚本,按下面的路径走:

  1. 登录/注册一个新账号(展示数据从空开始);
  2. 进入首页,查看空状态;
  3. 点击记账按钮,输入一笔“早餐支出”,选择“餐饮”分类;
  4. 再记一笔“工资收入”;
  5. 回到首页,查看列表是否正确显示两条记录;
  6. 切到统计页,查看折线图和饼图是否同步变化;
  7. 回到设置页,点击“导出账单”,打开CSV文件展示内容。

这条路径走完,所有核心功能都展示到了,而且逻辑非常清楚,从“无数据”到“有数据”到“数据可视化”再到“数据导出”,每个环节紧密衔接,老师跟着你的节奏走,思路完全不会断。这个脚本一定要提前在真机或模拟器上完整走三遍以上,确保中间没有意外情况。

7.3 老师最爱问的十二个问题

根据我积累的经验,以下几类问题几乎百分之百会被问到,我建议你提前把答案写在纸上。这些问题如果你能答得像聊天一样自然,基本就稳了。

  1. 为什么选这个题目?(答:记账是高频刚需,涉及增删改查、图表统计、权限管理等完整的Android知识点,技术覆盖面广且复杂度适中。)
  2. 为什么用SQLite,不用MySQL?(答:Android端本地数据更适合嵌入式数据库,数据保存在手机本地,无需网络和服务器,个人记账隐私性也更好;如果未来要做多端同步,再考虑后端加MySQL。)
  3. 金额为什么用“分”存储?(答:避免浮点精度问题,统计汇总更精确。)
  4. 数据库升级怎么处理?(答:使用SQLiteOpenHelper的onUpgrade方法,通过版本号判断执行哪些ALTER TABLE语句,或者重建表并迁移数据。)
  5. 图表库是自己写的吗?(答:基于MPAndroidChart开源库二次开发,我主要处理了数据集的组装、坐标格式化、动画效果和刷新逻辑。)
  6. 你的App怎么保证用户数据安全性?(答:本地SQLite数据库文件加密方案,应用私有目录存储,不落外部公共空间;密码存储用MD5/SHA-256哈希加盐。)
  7. 多用户数据如何隔离?(答:bill表通过user_id字段区分不同用户,每次查询都带user_id条件,保证用户A看不到用户B的数据。)
  8. 应付大数据量时性能如何优化?(答:索引优化,例如date字段建立索引;分页查询,不要一次性加载全部记录;硬件层面可配合SQLite的WAL模式提升并发读写。)
  9. 如果让你做一个云同步功能,你怎么设计?(答:需要一个服务器端API,把本地数据序列化为JSON上传,服务端校验后再同步回客户端,并处理冲突策略。)
  10. RecyclerView和ListView的区别?(答:RecyclerView强制使用ViewHolder复用,支持LayoutManager切换布局方式,Item动画、局部刷新等机制更完善。)
  11. 线程和UI更新的关系?(答:Android不允许在子线程更新UI,所有UI操作必须在主线程执行;子线程执行耗时操作后通过runOnUiThread或Handler切换到主线程。)
  12. 你在源码的基础上做了哪些改进?(答:增加预算提醒、导出CSV、多账本切换、限制输入校验等增量功能。这个答案必须真实,做过的项目不需要背,临场讲出细节即可。)

7.4 答辩时的展示技巧

答辩本质上是一次口头技术分享,你在台上讲的内容要让老师觉得“这个学生是真的把项目吃透了”。技巧上,我有三条建议。

第一条,主动说出自己踩过的坑。比如你想说“数据库查询”,就顺带说“我一开始查询日期时用了LIKE模糊匹配,结果把17号的数据也算进去了,后来改成前缀匹配才解决”。这种内容极其加分,你展示的并不是“按照教程一次成功”,而是你真的调试过、思考过。

第二条,遇到不会答的问题也千万别慌。你可以说“这个问题我确实还没有深入考虑过,我的思路是……”。老师问到明显超出毕设范围的问题时,他们在意的往往不是标准答案,而是你的分析框架。哪怕说不完整,也不要直接说“不知道”。

第三条,避免过于复杂的术语轰炸。虽然我们是做Android项目,但老师里面可能也有不做移动端的,你尽量用“数据表”“查询条件”“界面刷新”这类通识词来沟通,讲清楚“为什么这么做”而不是“用了什么类什么方法”。

8. 一个额外的小建议

账本这个题材虽然看起来像普通的增删改查,但我一直觉得它其实是最适合作为毕设的题目之一:需求清晰不飘、功能丰富但可控、技术点覆盖够全、上手门槛适中。你认真做完一个记账本,Android的Activity/Fragment、SQLite、RecyclerView、开源图表库、权限管理、数据导出、自定义布局这些关键能力就全部涉及到了,找工作面试时还能拿出一个完整的项目来讲,一本万利。

最后分享一个小技巧:源码拿到手之后,先复制一份备份,再开始改。无论你准备做多少二次开发,都建议严格用法否则改崩了连退路都没有。我当时就是因为改了包名后发现layout资源里有一处引用了旧包名,直接编译失败,还好有备份,十分钟就恢复了。这个习惯,不只是做毕设,以后做任何项目都用得上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 3:22:42

月加班20小时却被嫌少?揭秘大厂工时与绩效的底层逻辑

“月加班20小时还被嫌少”&#xff0c;这句话放在多数行业里&#xff0c;怎么听都像段子。但发帖的是大厂员工&#xff0c;语气里满是“被警告”的无奈&#xff0c;评论区跟着一排“感同身受”。当你发现自己拼了命凑出来的加班时长&#xff0c;在上级眼里只是一笔不合格的账时…

作者头像 李华
网站建设 2026/10/2 3:22:19

TrustZone开发环境搭建实战:OP-TEE与BoostKit配置指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 3:22:10

基于RFID的风电塔筒螺栓松动智能检测系统设计与实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 3:21:32

Oracle数据泵impdp导入dump全攻略:从原理到高频报错排查

上周接到一个活&#xff0c;把生产库导出的一整套dump文件恢复到测试环境。原本以为就是impdp一条命令的事&#xff0c;结果从directory对象报错到表空间配额不足&#xff0c;前后折腾了两个多小时。事后我把这次导入过程重新复盘了一遍&#xff0c;又把以往做数据库迁移、测试…

作者头像 李华
网站建设 2026/10/2 3:21:14

YOLOv8裂缝检测实战:从数据集训练到工地部署

简介&#xff1a;基于YOLOv8的基建裂缝目标检测系统&#xff0c;是一份面向计算机视觉、人工智能、自动化等专业学生与从业者的毕设级Python项目。资源以裂缝检测为核心&#xff0c;覆盖从数据集准备、模型训练到结果评估的完整流程&#xff0c;代码经调试可运行&#xff0c;适…

作者头像 李华
网站建设 2026/10/2 3:21:03

动态MAC与静态MAC:交换机二层转发核心机制与排障实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华