简介:一份基于Android平台开发的个人健康管理系统「健康管家」毕业设计资源,面向计算机相关专业学生及需要实战练习的开发者,可作毕业设计、课程设计或期末大作业使用。系统覆盖健康数据记录、运动跟踪、饮食管理、健康提醒、数据分析报告与个性化设置六大模块,涉及Android编程、数据库管理与数据分析等综合技能,适合作为完整项目参考。
资源包共210个文件,整体大小约14.33MB。其中57个java源文件实现核心业务逻辑,79个xml文件负责界面布局与配置,32个png和8个jpg图片资源构建界面视觉,另含so动态库、jar依赖库及gradle构建脚本等。目录结构清晰,便于按模块检索和学习。
项目源码已通过严格调试,可直接运行。通过研读源码,可掌握Android应用从界面搭建到数据持久化的完整流程,理解运动传感器接入、健康报告生成等实现思路。目前已有68人学习,对准备毕业设计或提升Android实战能力具有较高参考价值。
1. 毕业设计选它,到底值不值:Android健康管家要做什么
打开Android Studio,新建一个项目,命名「HealthManager」,屏幕前的你大概率正坐在宿舍里,面前摆着三样东西:一台跑得动模拟器的笔记本、一份还没写完的任务书、和一个「想选个能做出来又能答辩」的题目。Android个人健康管理系统就是那种看起来不起眼、但每年都在毕业设计选题库里反复出现的项目。它不依赖后端服务器,不要求实时通信,核心就是把用户的健康数据——身高体重、步数、心率、血压、饮食记录——存进本地数据库,再用图表展示趋势,配上一个能看能点的界面。整套做下来,Android四大组件、SQLite、RecyclerView、图表库、文件读写这些高频考点全都能覆盖。对想拿源码直接改、再写出一份能过查重的文档的同学来说,它是性价比最高的那类选题。
2. 先想清楚再写代码:技术选型和项目结构怎么定
2.1 为什么是Android原生 + SQLite,而不是其他方案
做个人健康管理系统,最常见的技术路线是Android原生开发,Java或Kotlin二选一,数据层用SQLite。很多人在选题后会犹豫:要不要上Room?要不要接个后端?要不要用Flutter?
我的建议很直接:如果目标是顺利毕业、答辩论得清,就不要碰后端。个人健康管理系统在绝大多数任务书里的定位是「单机应用」,数据只存在用户手机上。一旦引入后端,答辩时你就要解释服务器部署、网络请求、数据同步、线程安全,任何一个环节卡住都会被动。SQLite是Android自带的轻量数据库,不需要装任何东西,一个SQLiteOpenHelper类就能完成建库、建表、增删改查,代码量少、逻辑直观,刚好匹配毕业设计的深度。
Room是Google官方推荐的ORM框架,它比SQLite少写很多样板代码,但在答辩时有个隐患:面试官可能直接问「你SQL写得怎么样」。如果你的项目里看不到一条原生SQL语句,这个问题就很难圆。我的做法是数据库操作全用SQLiteOpenHelper + 手写SQL,再把数据访问层封装成一个DbHelper类。这样既保留了SQL能力,又没牺牲代码整洁度。
Kotlin还是Java?如果你的编程基础还行,用Kotlin会让代码量少三分之一,协程处理异步也顺手;如果Java是你唯一熟练掌握的语言,就老老实实用Java。毕业设计追求的是「你能讲清楚」,不是「用了多新的技术」。两个都行,但别在选题阶段浪费时间纠结,尽快定下来开工。
2.2 项目目录结构:按包划分,别把全部代码堆进MainActivity
很多毕业设计源码的通病是:一个Activity三千行,onCreate里写满了全部逻辑,数据库操作、UI初始化、事件监听混在一起。这样的代码能跑,但文档不好写、答辩不好讲、导师看了皱眉。
我一般会建议把项目按包名划分好职责,整体结构如下:
app/src/main/java/com/example/healthmanager/ ├── activity/ # Activity类,只做界面交互和生命周期管理 │ ├── MainActivity.java │ ├── LoginActivity.java │ ├── AddRecordActivity.java │ └── ChartActivity.java ├── adapter/ # RecyclerView适配器,处理列表数据绑定 │ └── RecordAdapter.java ├── db/ # 数据库相关,所有SQL语句都放这里 │ ├── DBHelper.java │ └── RecordDao.java ├── entity/ # 实体类,对应数据库表结构 │ └── HealthRecord.java └── util/ # 工具类,日期格式化、数据校验等 └── DateUtils.java每个包只干一件事,类名做到「看到就知道干什么」。这样的目录结构对写毕业设计文档特别有利——你可以直接在文档里画一张包结构图,逐层解释每一层的作用,凑字数又显得有条理。
资源文件也不要忽略:layout目录下给每个Activity单独建一个XML布局文件,drawable里放圆角背景、图标等。如果你在Android Studio里新建项目时用了默认的activity_main.xml和values目录,记得把字符串和颜色抽到strings.xml和colors.xml里,不要直接在布局里硬编码色值。这一点导师问到「资源分离」时能加分。
2.3 引入哪些依赖:一个图表库和一套Material组件就够了
第三方库不要贪多。健康管理系统的核心展示是图表,所以我一般只引入MPAndroidChart和Material Components两个依赖。MPAndroidChart是目前Android平台上最成熟的开源图表库,折线图、饼图、雷达图都支持,接入步骤也简单。
dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.9.0' implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' implementation 'androidx.recyclerview:recyclerview:1.3.0' }Material Components主要提供FloatingActionButton、CardView这些现代UI组件,能让界面看起来不像课设水平。MPAndroidChart的版本用v3.1.0就行,这个版本在网上有大量现成配置案例,遇到问题搜得到答案。注意v3.1.0要求minSdkVersion不低于21,对应Android 5.0,这对绝大多数设备都不是问题。
依赖版本号建议以Android Studio里提示的稳定版为准,不必追逐最新。有些新版本需要额外的迁移步骤,对毕业设计周期来说是纯消耗。
3. 核心功能拆解:从建表到图表,把健康管家跑起来
3.1 数据库设计:健康记录表的结构与字段说明
健康管家的核心数据是「一条健康记录」。一条记录包含用户信息、记录日期、体重、心率、血压、运动时长、睡眠时长、备注。设计表结构时,字段宁多勿少,因为后端功能扩充时只需要改SQL,不用动UI。
先写实体类,对应表的每一列:
package com.example.healthmanager.entity; public class HealthRecord { private int id; // 主键,自增长 private String date; // 记录日期,格式 yyyy-MM-dd private double weight; // 体重,单位kg,保留一位小数 private int heartRate; // 静息心率,单位次/分钟 private int systolic; // 收缩压,mmHg private int diastolic; // 舒张压,mmHg private int stepCount; // 当日步数 private float sleepHours; // 睡眠时长,单位小时,允许小数 private String note; // 备注,文本类型 public HealthRecord() { } public HealthRecord(String date, double weight, int heartRate, int systolic, int diastolic, int stepCount, float sleepHours, String note) { this.date = date; this.weight = weight; this.heartRate = heartRate; this.systolic = systolic; this.diastolic = diastolic; this.stepCount = stepCount; this.sleepHours = sleepHours; this.note = note; } // 省略Getter和Setter方法 }实体类的属性类型要和数据库列类型一一对应,double对应REAL,int对应INTEGER,String对应TEXT,float也可用REAL。注意id是int,在插入数据时不设值,交给SQLite自增生成。这个类全程不包含任何业务逻辑,只用来承载数据,所以叫实体类。
3.2 建表与数据库操作:SQLiteOpenHelper的写法
数据库操作类继承SQLiteOpenHelper,这是Android官方提供的辅助类,封装了打开、升级数据库的逻辑。重写onCreate和onUpgrade两个方法即可:
package com.example.healthmanager.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DBHelper extends SQLiteOpenHelper { private static final String DB_NAME = "health.db"; private static final int DB_VERSION = 1; public DBHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { String sql = "CREATE TABLE health_record (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "date TEXT NOT NULL, " + "weight REAL, " + "heart_rate INTEGER, " + "systolic INTEGER, " + "diastolic INTEGER, " + "step_count INTEGER, " + "sleep_hours REAL, " + "note TEXT)"; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 版本升级时执行迁移逻辑 db.execSQL("DROP TABLE IF EXISTS health_record"); onCreate(db); } }DB_NAME定义数据库文件名,DB_VERSION是数据库版本号。onCreate只在数据库第一次创建时执行,所以建表语句写在这里。onUpgrade在版本号变化时触发,最常见的写法就是先删表再重建,但这样会清掉用户数据。如果你希望答辩时被问到「数据丢失怎么办」,可以自己补一段ALTER TABLE迁移,把真实生产环境的需求写在文档里。
3.3 增删改查:健康记录列表的RecyclerView实现
列表展示是健康管家最基础的界面,用RecyclerView来承载。它的核心是三件套:Adapter负责把数据绑定到每个Item,LayoutManager负责排列方式,ViewHolder负责复用视图。先看Adapter代码:
package com.example.healthmanager.adapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.example.healthmanager.R; import com.example.healthmanager.entity.HealthRecord; import java.util.List; public class RecordAdapter extends RecyclerView.Adapter<RecordAdapter.RecordViewHolder> { private List<HealthRecord> recordList; private OnItemClickListener listener; public interface OnItemClickListener { void onItemClick(HealthRecord record); } public RecordAdapter(List<HealthRecord> recordList, OnItemClickListener listener) { this.recordList = recordList; this.listener = listener; } @NonNull @Override public RecordViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_record, parent, false); return new RecordViewHolder(view); } @Override public void onBindViewHolder(@NonNull RecordViewHolder holder, int position) { HealthRecord record = recordList.get(position); holder.tvDate.setText(record.getDate()); holder.tvWeight.setText(String.valueOf(record.getWeight()) + " kg"); holder.tvHeartRate.setText(String.valueOf(record.getHeartRate()) + " 次/分"); holder.itemView.setOnClickListener(v -> listener.onItemClick(record)); } @Override public int getItemCount() { return recordList == null ? 0 : recordList.size(); } static class RecordViewHolder extends RecyclerView.ViewHolder { TextView tvDate, tvWeight, tvHeartRate; RecordViewHolder(@NonNull View itemView) { super(itemView); tvDate = itemView.findViewById(R.id.tv_date); tvWeight = itemView.findViewById(R.id.tv_weight); tvHeartRate = itemView.findViewById(R.id.tv_heart_rate); } } }onCreateViewHolder中通过LayoutInflater加载item_record.xml布局并创建ViewHolder。onBindViewHolder通过position从数据列表中取出对应记录,把字段显示到控件上,同时给整个itemView设置点击事件。getItemCount返回列表长度,这个值直接决定了RecyclerView渲染多少个Item。你看到代码里的OnItemClickListener是自定义接口,把点击逻辑从Adapter里剥离出去,Activity层去实现具体跳转,这样Adapter只做绑数据这一件事。
3.4 图表统计:用MPAndroidChart把体重趋势画出来
健康管理系统必须有统计数据展示,不然就不配叫「管家」。折线图是记录类应用最常用的图表,体重、心率、睡眠时长都可以用折线图呈现。在ChartActivity的onCreate中初始化图表:
package com.example.healthmanager.activity; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import com.example.healthmanager.R; import com.example.healthmanager.db.DBHelper; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.XAxis; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.github.mikephil.charting.formatter.IndexAxisValueFormatter; import java.util.ArrayList; import java.util.List; public class ChartActivity extends AppCompatActivity { private LineChart lineChart; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chart); lineChart = findViewById(R.id.line_chart); List<HealthRecord> records = DBHelper.getInstance(this).getAllRecords(); List<Entry> entries = new ArrayList<>(); List<String> dates = new ArrayList<>(); for (int i = 0; i < records.size(); i++) { HealthRecord record = records.get(i); entries.add(new Entry(i, (float) record.getWeight())); dates.add(record.getDate()); } LineDataSet dataSet = new LineDataSet(entries, "体重变化"); dataSet.setColor(getColor(R.color.blue)); dataSet.setCircleColor(getColor(R.color.blue)); dataSet.setLineWidth(2f); dataSet.setValueTextSize(10f); LineData lineData = new LineData(dataSet); lineChart.setData(lineData); XAxis xAxis = lineChart.getXAxis(); xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); xAxis.setLabelCount(5, false); xAxis.setValueFormatter(new IndexAxisValueFormatter(dates)); lineChart.getDescription().setEnabled(false); lineChart.setTouchEnabled(true); lineChart.setPinchZoom(true); lineChart.invalidate(); } }这里XAxis的setLabelCount(5, false)指横轴最多显示5个刻度标签,false表示即使空间富余也不强制满额显示,防止日期挤在一起。IndexAxisValueFormatter把数字索引映射成日期字符串。LineDataSet里setLineWidth控制线的粗细,setValueTextSize控制数据点数字大小。getDescription().setEnabled(false)是关掉图表右下角默认的「Description」水印,这个细节很多教程不提,实际上线后显得非常简陋。setTouchEnabled和setPinchZoom分别控制触摸移动和双指缩放,开启后用户在答辩现场可以直接上手操作,演示效果好很多。
图表加载前记得先确认数据库里有数据,否则显示一张空图。如果发现图表一直不刷新,多半是setData之后没调用invalidate(),这个坑后面避坑章节还会细讲。
4. 把源码变成「能拿出手的项目」:文档和代码规范别忽略
4.1 注释怎么写得像大厂代码,又不像抄的
毕业设计的源码评审环节,导师打开项目第一眼看的就是注释。全部中文注释可能显得业余,全英文又容易暴露真实水平。我的经验是:类注释和方法注释用中文写清楚「这个类负责什么」,核心代码行用简短英文注释标注意图,但不要每一行都加注释。
/** * 健康记录适配器 * 负责将健康记录列表绑定到RecyclerView界面 * 点击事件通过OnItemClickListener回调给Activity处理 */ public class RecordAdapter extends RecyclerView.Adapter<RecordAdapter.RecordViewHolder> {那种「int i = 0; // 定义整数i」的逐行注释只会让代码显得更水。真正有效的注释说明业务逻辑,比如「// 首次安装时初始化默认数据,避免图表空白」。答辩时导师问起某段代码,你能顺着注释把思路讲出来,就说明这项目是你自己写的。
4.2 README和数据库设计文档:任务书之外必备的两份文档
毕设文档不是只有论文。一份规范的README.md放在项目根目录,能帮评阅老师快速了解项目;一份数据库设计说明放在论文附录里,能证明你做过系统设计。README我一般按固定模板写:
# Android个人健康管理系统 ## 项目简介 本系统面向个人用户,提供健康数据的记录、查询与可视化分析功能, 支持体重、心率、血压、步数、睡眠等维度的日常管理。 ## 开发环境 - Android Studio 版本 - JDK 版本 - 最低支持 Android 版本(minSdkVersion) ## 功能列表 1. 用户注册/登录 2. 健康记录的增删改查 3. 按日期范围筛选记录 4. 体重/心率趋势折线图 ## 技术栈 - 语言:Java - 数据库:SQLite(SQLiteOpenHelper) - UI:Material Components + RecyclerView - 图表:MPAndroidChart v3.1.0 ## 运行步骤 1. 使用Android Studio打开项目根目录 2. 等待Gradle同步完成 3. 连接真机或启动模拟器 4. 点击Run运行应用数据库设计文档则画一张E-R图,列出每张表的字段类型、主键、索引,再加上几条核心SQL语句的说明。对于单表项目,一张health_record表就够;如果你加了用户表,就要说明两张表的关系。用数据库设计文档配合源码里的DBHelper类,导师能一眼看出你的设计能力。
4.3 版本管理:从第一天就用Git,别等最后打包
毕业设计最常见的翻车方式之一:改了一版代码,没备份,然后新代码跑不起来了,原来的版本找不回来。哪怕你没有GitHub账号,也要在本地把项目初始化成Git仓库。
cd 项目目录 git init git add . git commit -m "初始版本:完成数据库和记录功能" # 之后每完成一个功能就提交一次 git add . git commit -m "新增:体重趋势折线图"每完成一个功能就提交一次,出问题能用git log查看历史提交、用git checkout回退到上一个可用版本,这是比任何「后悔药」都可靠的保护。Android Studio自带的Version Control面板能直接操作Git,不需要额外装任何工具。如果你把项目传到GitHub或Gitee的私有仓库,答辩前还能展示提交记录——那是「代码是你写的」最直接的证据。
5. 避坑:健康管家项目最常见的5个翻车现场
5.1 模拟器能跑、真机闪退:HTTP明文流量和FileProvider的坑
现象:自己的测试模拟器上一切正常,装到真机上启动就闪退,或者拍照上传图片功能一触发就崩溃。
原因:Android 9(API 28)开始默认禁止应用使用明文HTTP流量;Android 7.0(API 24)以后,App暴露file://类型的Uri会直接抛FileUriExposedException。很多教程里写的Uri.fromFile方式已经失效了,真机系统版本高,必踩。
解决:在AndroidManifest.xml的application标签下加一句android:usesCleartextTraffic="true"允许明文流量(仅测试够用)。涉及文件共享时,不要在Intent里直接传file://,要在res/xml下配置file_paths.xml并配合FileProvider生成content://类型的Uri。顺便提醒岗位面试时,FileProvider是Android中高级岗位常问的知识点,毕业设计里用一次,面试就多一个能讲的实践。
5.2 图表只显示第一次进入的数据:没有在数据变化后刷新
现象:先录入几条记录再进图表页,图是空白的;退出重进后数据出现了。
原因:MPAndroidChart的LineChart在setData之后需要一个invalidate()或notifyDataSetChanged()来触发重绘。Activity重新创建时走了一遍完整的onCreate,所以数据正常;没有重进时,图表对象还保留着旧状态。
解决:在setData之后统一调用lineChart.invalidate()。如果数据源是数组或List,传入LineDataSet后修改了底层列表内容,要调用lineChart.getData().notifyDataChanged()再invalidate()。另外,图表里的数据一定要从数据库重新查,不要复用上一个页面传递过来的临时对象。
5.3 数据库改了字段就闪退:版本号没升,onUpgrade没生效
现象:第一次安装运行正常,加了一个字段、改了一次表结构后,不卸载直接覆盖安装,启动就崩。
原因:SQLiteOpenHelper的onUpgrade只在版本号增加时触发。你改了建表SQL,但DB_VERSION仍然是1,系统认为没有变化,直接跳过升级逻辑,App启动后又用旧的表结构去查新字段,就崩了。
解决:每次变更表结构,一定要把DB_VERSION加1,同时在onUpgrade里写ALTER TABLE语句而不是DROP TABLE。比如:
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion < 2) { db.execSQL("ALTER TABLE health_record ADD COLUMN blood_sugar REAL"); } }这样用户的数据不会丢,代码也经得起导师问「版本升级怎么处理」。
5.4 图表X轴日期挤成一团:刻度数量不会算
现象:录了60天数据,X轴的日期全部挤在一起,黑乎乎一片,什么都看不清。
原因:MPAndroidChart的X轴默认会根据数据条目数自动分配刻度,60个日期标签全部画上去,屏幕宽度有限,必然重叠。
解决:使用setLabelCount来限制刻度数。一般手机屏幕宽度下5到7个刻度比较合适:
xAxis.setLabelCount(5, false);第二个参数force为false时,系统会在这几个刻度位置均匀分配标签,不会强制显示首尾值。如果你希望显示首尾日期,就设成true,但要接受标签可能重叠。另外,把XAxisPosition设为BOTTOM,标签放在底部比放在顶部好看,这也是和大多数图表应用一致的习惯。
5.5 界面卡顿:主线程直接跑了大型数据库查询
现象:数据量几百条时,点击查询按钮界面冻住一两秒,然后才跳到图表页。
原因:数据库查询、文件读写这类耗时操作放在了主线程(UI线程)执行。Android规定主线程不能做超过5秒的耗时操作,否则触发ANR(Application Not Responding)弹窗。
解决:用线程池或AsyncTask把数据库查询工作放到子线程。现在Kotlin协程更简洁,如果你用Java,用ExecutorService或Thread + runOnUiThread都可以:
new Thread(() -> { List<HealthRecord> list = dbHelper.getAllRecords(); runOnUiThread(() -> { adapter.updateData(list); }); }).start();这只是最基础的写法。如果日期范围查询、多表联查、图表数据聚合放在一起,数据量大了还是要考虑Room + LiveData这类带生命周期感知的方案。不过健康管家这种单机小应用,数据量一般在几千条以内,一个线程池足够应付。
6. 答辩演示有技巧:按场景演示,先跑核心流程再讲细节
答辩演示不要从头到尾点一遍按钮就结束。我发现最稳妥的顺序是:先展示注册登录,进入主界面录一条数据,把新增的记录在列表里指出来,然后切到图表页,指出刚录的数据在图表上对应的折线变化。这样「数据从产生到入库再到可视化」的完整链路就走了一遍,导师看到的不是零散界面,而是一个闭环系统。
演示完功能,主动打开项目源码,展示一件事:数据库建表语句和Activity里的图表配置代码。前者证明数据层是你自己设计的,后者证明核心展示功能是你控制的。如果被问到「用过哪些开源库」,就如实说用了MPAndroidChart和Material Components,再补一句「这个库的官方文档和源码我都看过,定制了坐标轴标签和数据点样式」,这话比「我引入了这个依赖」有分量得多。
答辩准备时还可以往这个方向延伸一点:把数据库层换成Room框架、引入协程做异步、增加计步器传感器读取真实步数、接入系统Health Connect接口同步健康数据。这些是最自然的功能升级方向,也是面试时能聊的进阶话题。Android面试里经常被问的「MVP/MVVM架构理解」,也能从当前项目说明为什么需要拆分、怎么拆分。
我毕业设计那年最后悔的事,就是把所有代码堆在一个Activity里,文档写到「技术难点」时无话可说。代码组织得清楚,文档和答辩都会轻松很多。希望帮到你。
本文还有配套的精品资源,点击获取