
简介面向高校学生与初级 Android 开发者的健康管理系统期末大作业完整源码包基于 Android Studio 工程构建使用 Java 与 XML 完成界面和业务逻辑。包含从项目配置到功能实现的完整工程文件涵盖 App 模块、Gradle 构建脚本及资源目录可直接导入开发工具运行适合作为移动应用课程设计或毕业设计的基础框架。压缩包内共 90 个文件整体大小约 903KB。其中 35 个 XML 文件用于界面布局和资源配置17 个 Java 文件承担主要功能实现另有 JPEG/WebP 图片资源、Gradle 构建脚本、ProGuard 混淆配置等文件分类明确目录结构符合标准 Android 工程规范便于按模块阅读和二次开发。已有 3369 人学习浏览代码完整且确保可运行。下载后你将获得一套无需额外配置的完整工程源码既能直接完成期末大作业提交也能通过阅读源码快速掌握 Android 项目组织方式、界面编写技巧和健康类应用的功能实现思路为后续开发提供可靠参考。1. 期末大作业源码到手别急着跑这份健康管理系统 App 先救急还是先踩坑很多同学从各种渠道下载到这份 Android Studio 健康管理系统期末大作业 App 源码后第一件事就是双击打开 Android Studio 点 Run结果等来的不是模拟器里的界面而是 Gradle 同步失败的红色报错和几十行看不懂的异常日志。这套资源本身覆盖了一门 Android 课设里最高频的几件事用户注册登录、健康参数录入、BMI 计算、历史记录列表和趋势展示数据层用 SQLite 做本地持久化不需要自己搭服务器就能交差适合期末时间紧、想快速出一个完整 Demo 的本科生也适合拿来做毕设的一期地基。但先泼一盆冷水——它不是一个解压就能跑的“绿点工程”导入后的包名、SDK 版本、数据库升级和中文乱码每一关都能卡住一拨人。这篇就按我实际拆这类课设源码的顺序从结构、跑通、改造到避坑完整走一遍。2. 先拆骨架再动手改工程结构、数据库表与依赖版本2.1 从工程目录反推功能边界拿到任何一份 Android 课设源码第一步绝不是马上编译而是先在 Project 视图下把目录结构过一遍。健康管理系统这类 App 的目录划分相当统一基本逃不开 activity、adapter、db、model、util 这几个包。用 tree 命令看一遍功能边界就清晰了health-management/ ├── build.gradle ├── settings.gradle └── app/ ├── build.gradle └── src/main/ ├── AndroidManifest.xml ├── java/com/example/health/ │ ├── activity/ # 登录、注册、主页、数据录入、历史记录 │ ├── adapter/ # RecyclerView 列表适配器 │ ├── db/ # SQLiteOpenHelper 子类与 DAO │ ├── model/ # User / HealthRecord 实体类 │ └── util/ # 校验工具、日期格式化 └── res/ ├── layout/ # 每个 Activity 对应一个 XML ├── drawable/ # 按钮圆角、图标资源 └── values/ # strings / colors / themes逻辑说明看 activity 包能知道这个 App 有几个页面看 db 包能知道数据从哪来看 adapter 包能确认列表页用没用 RecyclerView。我拿到手一般先数 activity 数量再对照 AndroidManifest.xml 里注册的组件如果 Manifest 里声明了某个 Activity 而源码里找不到或者反过来说明这份工程被人改到一半跑起来必崩。参数说明课程设计考核的重点通常分布在三个位置——登录注册逻辑、数据库增删改查、列表展示。这三块分别在 activity、db、adapter 包里改代码前优先精读这三个包能省掉大量翻文件的时间。2.2 核心表结构用户表与健康记录表的设计逻辑健康管理系统的数据层几乎都是 SQLite核心是两张业务表加一张扩展表。我拆过的同类课设源码里表设计基本长这样CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL, height REAL DEFAULT 0, weight REAL DEFAULT 0, create_time TEXT DEFAULT (datetime(now,localtime)) ); CREATE TABLE health_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, height REAL NOT NULL, weight REAL NOT NULL, heart_rate INTEGER, blood_pressure TEXT, blood_sugar REAL, record_time TEXT DEFAULT (datetime(now,localtime)), FOREIGN KEY (user_id) REFERENCES user(id) ); CREATE TABLE step_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, date TEXT NOT NULL, steps INTEGER DEFAULT 0, target INTEGER DEFAULT 6000, UNIQUE(user_id, date) );逻辑说明user 表把身高体重冗余了一份同时 health_record 表也记身高体重这不是写重了而是有意为之——user 表存的是注册时的基础资料health_record 每次录入都留一份快照这样后续做趋势分析时才能看出身高体重随时间的波动。record_time 用 datetime(now,localtime) 存文本比存毫秒时间戳更直观SQLite 查某天记录时直接 LIKE 就行。参数说明UNIQUE(user_id, date) 是步数表的关键约束它保证同一个用户同一天只有一条步数记录插入新数据时用 INSERT OR REPLACE 或先查后插都行。实际改这份源码时如果你想加“饮食记录”或“用药提醒”模块复制 health_record 表结构改几个字段名是最快的路子SQLiteOpenHelper 的版本号记得加 1。2.3 依赖版本与 API 级别决定你能跑在哪台设备上打开 app/build.gradle 先看三行配置compileSdk、minSdk、targetSdk。健康管理这类课设源码大概率是前几年写的常见组合是 compileSdk 30 / minSdk 21 / targetSdk 30而你现在装的 Android Studio 可能已经要求 AGP 8.0 以上这里最容易翻车。android { compileSdk 34 defaultConfig { applicationId com.example.health minSdk 21 targetSdk 34 versionCode 1 versionName 1.0 } } dependencies { implementation androidx.appcompat:appcompat:1.6.1 implementation com.google.android.material:material:1.9.0 implementation androidx.recyclerview:recyclerview:1.3.0 }逻辑说明targetSdk 从 30 升到 34 不是无痛升级。Android 11 引入了包可见性规则Android 13 加了通知权限 POST_NOTIFICATIONSAndroid 14 对前台服务类型做了强校验。健康管理系统如果做了每日步数提醒这类功能targetSdk 升级后不处理这些新规则功能会静默失效。参数说明AGP 和 Gradle 的版本匹配是固定搭配AGP 7.4.2 配 Gradle 7.5AGP 8.0 配 Gradle 8.0乱配的结果是 Gradle sync 时报错 “Minimum supported Gradle version is X”。改版本时先查 gradle-wrapper.properties 里的 distributionUrl再去 Android Studio 里同步别先动 build.gradle。3. 把这套源码跑起来导入、Gradle 同步、设备部署三关怎么过3.1 导入工程选错目录是最低级的错误Android Studio 导入工程不是直接 Open 最外层文件夹而是要选中包含 build.gradle 的那一层。这份健康管理系统源码解压后可能套了两层目录很多人直接把整个压缩包文件夹拖进去结果 Android Studio 提示 “Gradle project not found”。cd health-management ls -la # 确认当前目录有没有 settings.gradle 和 build.gradle # 有 settings.gradle 的目录才是工程根目录逻辑说明正确做法是 File → Open然后定位到包含 settings.gradle 的目录。老版本工程可能没有 settings.gradle只有根目录 build.gradle那也可以识别。导入后 Android Studio 会自动开始 Gradle sync此时别急着点 Run。参数说明如果 Open 时提示 “Gradle DSL method not found”大概率是工程用了极老的 Gradle 版本需要手动改 gradle-wrapper.properties 里的 distributionUrl常见的稳妥版本是 gradle-7.5-bin.zip。改完重新 Sync下载会比较慢耐心等。3.2 Gradle 同步失败镜像仓库与下载超时在国内网络环境下Gradle 同步失败九成是仓库地址访问超时。新工程默认走 google() 和 mavenCentral()这两个源在国内直连不稳解决方式是配置阿里云镜像。Android Studio 现在用 settings.gradle 管理仓库把镜像源加在 google() 前面pluginManagement { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/central } maven { url https://maven.aliyun.com/repository/gradle-plugin } google() mavenCentral() } } dependencyResolutionManagement { repositories { maven { url https://maven.aliyun.com/repository/google } maven { url https://maven.aliyun.com/repository/central } google() mavenCentral() } }逻辑说明Gradle 会按声明顺序依次尝试下载镜像源放在最前面能优先命中。注意老版本工程没有 settings.gradle仓库地址写在根目录 build.gradle 的 allprojects 块里写法是 repositories { maven { url ... } }改造思路一样。参数说明如果同步进度条卡在 “Downloading Gradle-xxx.zip”那是 Gradle 发行包本身下载不动直接手动下载对应版本的 zip 放到用户目录下的 gradle/wrapper/dists 里比干等有效。还有一类情况是电脑上配了无效的 HTTP ProxyAndroid Studio 没走直连导致下载全部超时检查 Settings → HTTP Proxy 设为 “No proxy” 再重试。3.3 AVD 硬件加速与真机部署模拟器起不来是健康管理系统源码交付前的另一个高频故障典型报错是 “x86_64 emulation currently requires hardware acceleration”。原因很简单x86 镜像需要 HAXM 或 Hyper-V 加速笔记本 BIOS 里虚拟化没开。# 检查 CPU 虚拟化是否开启 emulator -accel-check # 输出 HAXM is not installed 时通过 SDK Manager 装 Intel x86 Emulator Accelerator逻辑说明装完 HAXM 后重启模拟器一般能过。如果电脑是 AMD 平台走的不是 HAXM 而是 Windows Hypervisor Platform需要到 Windows 功能里勾选开启再在 AVD 的配置里把 Graphics 设为 Hardware。参数说明模拟器上跑健康管理这类带 SQLite 的工程没大问题唯一难受的是传感器模块——AVD 没有真实的计步传感器步数功能在模拟器上要么返回 0 要么报错。要测步数功能应该用真机。真机部署时打开开发者选项和 USB 调试Android Studio 识别到设备后直接 Run 即可。3.4 Android 6.0 之后的动态权限处理健康管理系统如果包含计步、身体传感器或读写外部存储的代码而工程又是早期模板Manifest 里可能只声明了权限没有做运行时请求。Android 6.0 以上直接运行时态授权不然功能静默失效App 不崩但拿不到数据。private void requestSensorsPermission() { if (checkSelfPermission(Manifest.permission.ACTIVITY_RECOGNITION) ! PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{ Manifest.permission.ACTIVITY_RECOGNITION, Manifest.permission.BODY_SENSORS }, 1001); } } Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode 1001 grantResults.length 0 grantResults[0] PackageManager.PERMISSION_GRANTED) { // 权限拿到后再注册步数监听 } }逻辑说明这段代码把权限请求放在功能入口处而不是 MainActivity 的 onCreate 里一次性请求全部权限这是新权限规范要求的——用户必须理解为什么需要这个权限。ACTIVITY_RECOGNITION 是计步功能必需的BODY_SENSORS 是心率检测必需的健康管理系统通常两个都要。参数说明Android 13targetSdk 33还多了一个 POST_NOTIFICATIONS 通知权限如果 App 有身体指标异常提醒的功能这个权限不请求通知栏永远静默。检查这份源码时Manifest 里看到 uses-permission 的都要对照查一遍运行时代码。4. 核心功能模块梳理登录会话、健康录入、BMI 计算、历史列表与步数传感器4.1 注册登录逻辑MD5 加密与 Session 保持健康管理系统第一个要讲的模块是登录注册它决定用户信息进出数据库的方式。早期课设源码的密码存法五花八门最常见的是明文直存好一点的会做 MD5。实际改这份源码时我建议至少把密码字段用 MD5 处理虽然 MD5 早已不算安全加密但作为期末作业体现设计意识足够了public String md5(String input) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(input.getBytes(UTF-8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (Exception e) { return input; } } // 登录成功后保存会话 SharedPreferences sp getSharedPreferences(health_pref, MODE_PRIVATE); sp.edit().putInt(userId, user.getId()).apply();逻辑说明登录页面拿到输入的用户名密码后先去 user 表按用户名查记录再把查到的密码字段做同样的 MD5 比对。登录成功后把 userId 写进 SharedPreferences这就是最轻量的 Session 保持方案。注意 getSharedPreferences 用的是 MODE_PRIVATE不推荐用 MODE_WORLD_READABLE 之类的老写法。参数说明MD5 的坑在于不同文本拼接盐值后结果不同注册和登录两端必须用同一套拼接规则不然永远登录不上。比如注册时存 md5(密码) 和登录时算 md5(密码) 是一致的但如果你加盐就务必保证两个入口都执行同样的加盐拼接。4.2 健康数据录入表单校验与入库录入页面是这个 App 数据链路的入口字段包括身高、体重、心率、血压、血糖。课设源码最容易在数值校验上偷懒直接 String → float 强转用户输入非法字符时当场崩溃体验极差而且答辩时老师最喜欢故意输一个负数测试private HealthRecord buildRecordFromInput() { String heightStr etHeight.getText().toString().trim(); String weightStr etWeight.getText().toString().trim(); if (heightStr.isEmpty() || weightStr.isEmpty()) { Toast.makeText(this, 身高和体重不能为空, Toast.LENGTH_SHORT).show(); return null; } float height Float.parseFloat(heightStr); float weight Float.parseFloat(weightStr); if (height 50 || height 250) { Toast.makeText(this, 身高范围应在 50-250cm 之间, Toast.LENGTH_SHORT).show(); return null; } HealthRecord record new HealthRecord(); record.setUserId(currentUserId); record.setHeight(height); record.setWeight(weight); return record; }逻辑说明buildRecordFromInput 把表单控件值转成实体对象逻辑链是“非空校验 → 数值解析 → 范围校验 → 返回对象”。注意 Float.parseFloat 本身会抛 NumberFormatException正规做法要把整个解析包在 try-catch 里上面代码简化处理了但你在改造时最好补上异常捕获和 Toast 提示。参数说明范围阈值 50-250cm 是对应健康管理场景的如果你要看欧洲标准可以改到 200课设场景用中国成人身高参考范围没问题。更重要的是思路——所有用户可输入的数值都要设边界这是健康类 App 数据可信度的基础。4.3 BMI 计算与健康评估公式和分级阈值BMI 是健康管理系统里得分最高的功能点公式简单但分级阈值有讲究。国内成人 BMI 标准和中国标准略有差异源码里大概率直接用 kg/m² 公式重点看它用的评判表是哪套public String evaluateBmi(float heightCm, float weightKg) { float heightM heightCm / 100f; float bmi weightKg / (heightM * heightM); if (bmi 18.5) return 偏瘦; if (bmi 24) return 正常; if (bmi 28) return 超重; return 肥胖; }逻辑说明这套阈值是亚洲人群常用的四档划分和世界卫生组织的标准有差异——WHO 把 25 到 30 才算超重。如果是给中国人做课设用这套更合理。注意浮点精度问题身高 175cm、体重 70kg 算出来是 22.86落在“正常”区间误差在小数点后两位不影响结论。参数说明BMI 的数值计算不是难点难点在显示策略。源码里如果只在 TextView 上淡地显示数字可以优化成一个带颜色的评价卡片——偏瘦显示蓝色、正常显示绿色、超重显示橙色、肥胖显示红色答辩时视觉冲击力会强很多。4.4 历史记录列表RecyclerView 与游标适配健康管理系统一定有一个历史记录列表页把 health_record 表按时间倒序列出来。早年的课设源码会直接用 SimpleCursorAdapter 绑 ListView换个 RecyclerView 会显得更有诚意public ListHealthRecord getAllRecords(int userId) { ListHealthRecord list new ArrayList(); SQLiteDatabase db getReadableDatabase(); Cursor cursor db.rawQuery( SELECT * FROM health_record WHERE user_id? ORDER BY record_time DESC LIMIT 50, new String[]{String.valueOf(userId)}); while (cursor.moveToNext()) { HealthRecord r new HealthRecord(); r.setId(cursor.getInt(cursor.getColumnIndexOrThrow(id))); r.setHeight(cursor.getFloat(cursor.getColumnIndexOrThrow(height))); r.setWeight(cursor.getFloat(cursor.getColumnIndexOrThrow(weight))); list.add(r); } cursor.close(); return list; }逻辑说明这段查询把游标转成了实体列表再交给 RecyclerView.Adapter 渲染。ORDER BY record_time DESC 是列表页的关键排序逻辑LIMIT 50 防止数据量大了卡顿。注意 getColumnIndexOrThrow 如果表结构改了没同步会直接抛异常这正是 SQLite 升级时要小心的点。参数说明cursor.close() 必须执行否则泄漏游标导致 Repeated queries 越来越慢。这条查询是只读的用 getReadableDatabase() 就够没必要拿写锁。4.5 步数传感器SensorManager 注册与注销的对称性步数模块是健康管理系统里最“值钱”的功能也是翻车重灾区。TYPE_STEP_COUNTER 返回的是设备开机以来累计的总步数不是当天步数你需要自己保存上次的值做差值private SensorEventListener stepListener new SensorEventListener() { Override public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() Sensor.TYPE_STEP_COUNTER) { long totalSteps (long) event.values[0]; long todaySteps totalSteps - lastTotalSteps; tvSteps.setText(String.valueOf(todaySteps)); } } Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; Override protected void onResume() { super.onResume(); lastTotalSteps prefs.getLong(last_total_steps, 0L); sensorManager.registerListener(stepListener, sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER), SensorManager.SENSOR_DELAY_UI); } Override protected void onPause() { super.onPause(); sensorManager.unregisterListener(stepListener); }逻辑说明TYPE_STEP_COUNTER 从设备开机开始计数重启后归零因此 lastTotalSteps 必须持久化到 SharedPreferences每次 onResume 时取出来算差值。SENSOR_DELAY_UI 是适合 UI 刷新的采样频率大约 200ms 一次计步精度不影响但功耗控制更友好。参数说明onPause 里 unregisterListener 是必写的漏掉的话 Activity 退到后台传感器还在回调电量和内存都会被拖垮这也是内存泄漏检测工具经常报的问题。模拟器上没有真实计步传感器getDefaultSensor 可能返回 null要加空指针判断。5. 移植和改造的避坑清单资源重复、数据库升级、中文乱码与 SDK 错配5.1 android studio 资源重复错误Merge 阶段 R 文件冲突现象Gradle sync 通过编译时报错 “Duplicate resources” 或 “The resource is defined multiple times”定位到 res 目录下同名 drawable 或 layout。原因这份健康管理系统源码可能被多人传阅有人直接把另一个工程的图片拖进了 drawable 目录两张图同名或者 layout 文件名和 values 里的某个 id 重复。还有一种情况是 res 目录下存在相同文件名的不同格式副本比如 icon.png 和 icon.jpeg 同时存在。解决打开 res 目录按文件名排序肉眼排查同名资源。确定哪个是当前工程用的删掉多余的然后 Build → Clean Project 再 Rebuild。如果资源名大量重复且来自不同模块检查 build.gradle 里有没有多个 module 引用了相同资源名前缀必要时用 tools:override 显式声明。从那以后我每次解压课设源码第一件事就是全盘搜索 res 目录下是否有重复文件名这招能避免至少半小时的编译期折磨。5.2 数据库版本升级onUpgrade 不写迁移代码直接崩现象手机上已经装了通过这份源码包生成的旧版本 App直接用 Android Studio 装新版本启动即闪退Logcat 里报 SQLiteException: no such column。原因开发时改了 health_record 表结构新增了 blood_sugar 字段但 SQLiteOpenHelper 的版本号和 onUpgrade 方法没同步。SQLite 的特性是旧表不会自动加列App 升级后执行旧 SQL 查不存在的字段直接抛异常。解决把 SQLiteOpenHelper 构造函数的 version 参数加 1在 onUpgrade 里写增量迁移语句Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (oldVersion 2) { db.execSQL(ALTER TABLE health_record ADD COLUMN blood_sugar REAL DEFAULT 0); } if (oldVersion 3) { db.execSQL(ALTER TABLE health_record ADD COLUMN cholesterol REAL DEFAULT 0); } }逻辑说明onUpgrade 的增量迁移是健康管理 App 改表的核心知识点。每发布一版就加一个 if 分支用 oldVersion 判断当前设备处于哪个版本逐级执行 ALTER TABLE。这样用户从任何旧版本升级到新版本都能走通而不是只处理相邻版本。 参数说明ALTER TABLE 只能加列不能改列类型如果某个字段要改名常规做法是重建整张表——建新表、拷贝数据、删旧表、重命名这套流程可以在一个事务里完成。课设场景基本只需要 ALTER TABLE ADD COLUMN够用。5.3 模拟器与真机路径差异数据库到底存在哪现象代码里明明能看到 health.db 文件但用 Android Studio 的 File Explorer 打开 /data/data 目录是空的或者报 Permission denied。原因Android 6.0 之后 app 私有目录 /data/data/包名/databases 默认不可随意浏览非 debuggable 的 App 连 run-as 都进不去。模拟器和真机的文件系统结构相同但模拟器上你看到的是 AVD 的虚拟磁盘真机上是物理存储。解决开发阶段用 debuggable 的包安装然后用 adb 工具访问adb shell run-as com.example.health ls databases/ adb shell run-as com.example.health cp databases/health.db /sdcard/Download/ adb pull /sdcard/Download/health.db ./health_backup.db逻辑说明run-as 只在 App 是 debug 签名时才能访问私有目录Release 包会直接拒绝。导出后用 SQLite 可视化工具打开 health_backup.db检查的字段是否真实写入。 参数说明别忘了加 --user 参数处理多用户设备部分设备需要 adb shell run-as com.example.health --user 0。这一步是答辩前自查的必经环节评委问“数据存哪了”能直接答上。5.4 buildToolsVersion 与 compileSdk 错配Gradle 版本匹配表现象Gradle sync 报警告 “Failed to find Build Tools revision 30.0.2” 或者 “Namespace not specified”。原因工程里指定了某个 buildToolsVersion 但本机 SDK Manager 里没有装对应版本。另外 AGP 8.0 之后强制要求 namespace 写在 build.gradle 里老工程里没有这个字段会直接同步失败。解决打开 SDK Manager 勾选缺失的 Build Tools 版本或干脆把 build.gradle 里的 buildToolsVersion 删掉让 Gradle 自动选择默认版本。加一行 namespace 声明android { namespace com.example.health }逻辑说明compileSdk 决定编译用哪个 API 版本buildToolsVersion 决定构建工具版本两者独立但 AGP 版本对 Gradle 版本有硬性要求。从 AGP 7.0 开始build.gradle 里的 namespace 取代了 AndroidManifest.xml 里的 package 属性老工程缺这个字段必须补。 参数说明遇到版本匹配问题最快的方式是不自己去猜组合直接看 gradle-wrapper.properties 里的 Gradle 版本和 AGP 版本是否满足匹配表AGP 7.4 对应 Gradle 7.5AGP 8.0 对应 Gradle 8.0。升级 AGP 通常意味着同时升级 Gradle两个文件都要改。5.5 中文字符乱码zip 解压编码与硬编码文案现象导入工程后 Java 文件里的中文注释全部变成乱码或者编译报错 “unmappable character for encoding UTF-8”界面上的按钮文字显示成“???”。原因这份健康管理系统源码大概率在 Windows 环境打包部分文件用 GBK 编码保存Android Studio 默认按 UTF-8 读取编码不一致导致乱码。另一个场景是有人为了省事直接把中文写死在 Java 代码里没走 strings.xml。解决在 Android Studio 里把全局编码统一为 UTF-8File → Settings → Editor → File Encodings把 Global Encoding 和 Project Encoding 都设为 UTF-8。如果源码文件已经是 GBK 且内容多用命令行批量转码find . -name *.java -exec iconv -f GBK -t UTF-8 {} -o {}.utf8 \;逻辑说明iconv 把整个工程的 Java 文件从 GBK 转成 UTF-8转完再把原文件替换回来。注意转换前先做 git 备份转码如果中间断了会产生半乱码文件。 参数说明正确做法是把所有界面中的中文文案抽到 res/values/strings.xmlJava 代码里引用 string 资源这样不仅解决编码问题也让界面文案统一修改更方便。这套做法也是答辩时体现工程规范的重要加分项。6. 往毕业设计方向改图表展示、数据链路验证与代码规范收尾6.1 用 MPAndroidChart 画健康趋势图期末作业交一张静态表格勉强过关毕设级别的健康管理必须有趋势图。把每一条 health_record 的 BMI 数值画在折线图上评委一眼就能看出你做了数据分析。这个库接入简单在 build.gradle 的 dependencies 里加一行implementation com.github.PhilJay:MPAndroidChart:v3.1.0ListEntry entries new ArrayList(); for (int i 0; i records.size(); i) { HealthRecord r records.get(i); entries.add(new Entry(i, calculateBmi(r))); } LineDataSet dataSet new LineDataSet(entries, BMI 走势); LineChart chart findViewById(R.id.chart_bmi); chart.setData(new LineData(dataSet)); chart.invalidate();逻辑说明entries 的 x 轴是记录序号y 轴是 BMI 值。LineDataSet 负责折线样式LineChart 是控件本体最后 invalidate() 刷新画布。图表库自带的动画效果能让整个页面质感提升一大截。 参数说明注意 MPAndroidChart 需要连 JitPack 仓库要在 settings.gradle 的 repositories 加一条 maven { url https://jitpack.io }。老版本的库对 targetSdk 兼容性一般建议用 v3.1.0 之后的版本。6.2 数据链路验证从录入到重启的完整自查改造完成后别急着交先走一遍全流程测试注册新用户 → 退出登录 → 重新登录 → 录入一条健康数据 → 查看历史列表 → 杀掉 App 进程 → 重新打开 → 确认数据还在。这条链路能排查掉 90% 的持久化问题。进一步用 SQLite 命令行确认adb shell run-as com.example.health sqlite3 databases/health.db sqlite SELECT height, weight, record_time FROM health_record ORDER BY record_time DESC LIMIT 5;sqlite3 命令在部分模拟器上不存在但 Android Studio 自带的 Database Inspector 也能直接查看数据库内容一样能验证表结构和数据完整性。6.3 代码规范变量命名、注释与硬编码清理最后把 activity 包里的代码过一遍把含糊的变量名改掉比如把 m1、m2 改成 weightKg、bodyFatRate把散落在 Java 代码里的中文字符串迁移到 strings.xml。这一步的价值在于答辩时的查看代码环节——一个改名成本很低但效果极好的工程代码风格决定了评委怎么定义“态度”。从那以后我每次拿到课设源码第一件事不是跑起来而是先把包名、targetSdk 版本和数据库版本号全部确认一遍再开始动任何代码。这三个值决定了后面要做多少适配工作提前查清楚能少熬两个通宵。希望帮到你。本文还有配套的精品资源点击获取