尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Android课设实战:星座配对App从MVC架构到SQLite完整实现

Android课设实战:星座配对App从MVC架构到SQLite完整实现 简介这是一份面向高校移动应用开发课程与Android初学者的星座配对App课程设计完整文档以Java为开发语言、Android Studio为开发环境采用MVC架构与SQLite数据库适合作为期末大作业或毕业设计的参考范本。文档围绕星座信息展示、星座运势查询、星座配对分析和用户个性化设置四大功能展开完整覆盖项目背景、开发技术与环境、详细设计、运行演示及心得体会等章节并具体讲解GuideActivity引导页、ViewPager轮播、LuckBean与ParnterAnalysisBean等实体类以及各页面Activity的逻辑实现8635字篇幅结构清晰、论述充分。资源包共1个doc文件约11.03MB内容为可直接参考的论文式文档便于读者对照梳理需求分析、架构设计与代码组织思路快速搭建自己的课程设计框架。目前已有284人学习下载适合需要完整赛题方案与写作模板的Android学习者。1. 从一份 8635 字的星座配对报告说起Android 课设怎么做出「能交差又能吹」的完整度每年学期末总有一批同学在 Android Studio 里对着一个半成品 App 发愁功能能跑但论文写不出深度答辩时被问「你的架构是什么」就卡壳。星座配对报告这个题目之所以年年有人选恰恰因为它把「数据录入、算法匹配、结果展示、历史记录」这条完整链路压缩在了一个可控的工程量里——十二星座的配对逻辑天然适合用矩阵或规则表实现SQLite 存用户和记录MVC 把界面和业务拆开最后再配一份八千字左右的论文从项目背景一路写到心得体会。这套组合拳打下来既不会像电商 App 那样被支付和订单状态机拖死也不会像纯工具类 App 那样单薄到撑不起论文篇幅。这篇文章面向的是正在做 Android 课程设计、需要一份「能跑、能讲、能写」的完整项目的同学。我会按真实开发顺序把开发环境搭建、MVC 分层设计、SQLite 建库建表、配对算法实现、运行演示和论文写作要点全部拆开讲。你不需要有很深的 Java 基础但至少要能看懂类和接口如果连findViewById都没用过建议先把 Android 基础控件过一遍再回来。下面所有内容都基于 Android Studio Java SQLite 这套最稳的组合不引入 Kotlin、Room 或 Compose因为课设场景下越少的新技术意味着越少的答辩风险。2. 开发环境与项目骨架从 Android Studio 安装到 MVC 三层落地2.1 Android Studio 安装与中文环境配置的取舍Android Studio 的安装本身不复杂但有几个版本坑值得提前说。官网下载时优先选带Android Studio字样的稳定版不要选 Canary 或 Beta 通道课设不需要尝鲜。安装向导里记得勾选 Android SDK、Android SDK Platform 和 Android Virtual Device 三项SDK 版本建议选 API 34 或 API 33再往下的版本在新版 Android Studio 里可能找不到对应的构建工具。安装完成后第一件事是配 Gradle。国内网络环境下 Gradle 下载经常卡住常见做法是在项目根目录的gradle.properties里加代理配置或者直接用阿里云的 Maven 镜像。具体操作是在settings.gradle的repositories块里把google()和mavenCentral()替换成镜像地址。这一步不做后面每次同步都可能等十分钟以上。至于「Android Studio 怎么设置中文」官方并没有内置中文语言包网上流传的汉化插件大多停留在旧版本装上去反而可能导致菜单错乱。我的建议是保持英文界面把常用操作的快捷键记熟比如ShiftF10运行、CtrlAltL格式化代码比汉化实在得多。2.2 用 MVC 三层架构拆星座配对 App 的包结构MVC 在 Android 里不是官方强制架构但课设论文里写 MVC 三层架构是加分项因为它能体现你有分层意识。具体落地时我一般这样分包com.example.constellation ├── model // 数据模型与业务逻辑 │ ├── Constellation.java │ ├── MatchResult.java │ └── MatchCalculator.java ├── view // Activity 与自定义 View │ ├── MainActivity.java │ ├── MatchActivity.java │ └── HistoryActivity.java ├── controller // 业务调度与数据访问 │ ├── MatchController.java │ └── UserController.java └── db // SQLite 帮助类 └── DBHelper.javamodel层放实体类和配对算法view层只负责界面渲染和用户输入转发controller层接收view的请求、调用model和db、再把结果回传给view。这样拆的好处是答辩时你可以指着包结构说「这是 MVC 三层」而不是把所有代码堆在一个 Activity 里。2.3 项目创建时的关键配置项新建项目时选Empty Views Activity语言选 Java最低 SDK 选 API 24Android 7.0这样覆盖率够高又不会用到太老的 API。build.gradle里需要加两个依赖androidx.recyclerview:recyclerview用于历史记录列表androidx.cardview:cardview用于结果卡片展示。这两个库版本不用追新用 Android Studio 自动补全的默认版本即可。dependencies { implementation androidx.appcompat:appcompat:1.6.1 implementation androidx.recyclerview:recyclerview:1.3.2 implementation androidx.cardview:cardview:1.0.0 implementation com.google.android.material:material:1.11.0 }appcompat提供 Activity 兼容支持recyclerview是历史记录列表的核心cardview让配对结果看起来像一张卡片而不是裸 TextView。material库用于日期选择器和按钮样式。这四个依赖加起来不到 2MB不会拖慢构建速度。注意如果同步时提示Failed to resolve先检查settings.gradle里的仓库地址是否被镜像覆盖再检查网络是否能访问 Maven 仓库。不要盲目升级 Gradle 插件版本课设环境稳定比新特性重要。3. SQLite 建库建表与配对算法的 Java 实现3.1 用 DBHelper 建三张表用户、星座、配对记录SQLite 在 Android 里通过SQLiteOpenHelper管理我一般建三张表user存用户基本信息constellation存十二星座的静态数据match_record存每次配对的历史记录。建表语句写在onCreate里升级逻辑写在onUpgrade里。public class DBHelper extends SQLiteOpenHelper { private static final String DB_NAME constellation.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) { // 用户表id 自增name 昵称gender 性别birthday 出生日期 db.execSQL(CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, gender TEXT, birthday TEXT)); // 星座表id 主键name 星座名start_date/end_date 日期范围 db.execSQL(CREATE TABLE constellation ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, start_date TEXT, end_date TEXT)); // 配对记录表id 自增user_id 外键target_sign 目标星座score 匹配分create_time 时间 db.execSQL(CREATE TABLE match_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, target_sign TEXT, score INTEGER, create_time TEXT)); initConstellationData(db); } private void initConstellationData(SQLiteDatabase db) { String[][] signs { {1, 白羊座, 03-21, 04-19}, {2, 金牛座, 04-20, 05-20}, {3, 双子座, 05-21, 06-21}, {4, 巨蟹座, 06-22, 07-22}, {5, 狮子座, 07-23, 08-22}, {6, 处女座, 08-23, 09-22}, {7, 天秤座, 09-23, 10-23}, {8, 天蝎座, 10-24, 11-22}, {9, 射手座, 11-23, 12-21}, {10, 摩羯座, 12-22, 01-19}, {11, 水瓶座, 01-20, 02-18}, {12, 双鱼座, 02-19, 03-20} }; for (String[] s : signs) { db.execSQL(INSERT INTO constellation VALUES (?, ?, ?, ?), s); } } Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DROP TABLE IF EXISTS user); db.execSQL(DROP TABLE IF EXISTS constellation); db.execSQL(DROP TABLE IF EXISTS match_record); onCreate(db); } }onCreate只在数据库第一次创建时调用所以初始化星座数据放在这里最合适。onUpgrade里直接删表重建是课设阶段的常见做法正式项目里应该写ALTER TABLE迁移但课设数据量小重建成本可以接受。initConstellationData用二维数组批量插入比写十二条INSERT语句清爽得多。3.2 配对算法的规则表实现从星座矩阵到分数计算星座配对的「算法」本质上是一张 12×12 的分数矩阵。网上流传的配对表版本很多我一般取一个折中方案同象星座火、土、风、水给高分相邻象给中等分对冲象给低分。具体实现时用一个MapString, Integer存分数key 是「星座 A-星座 B」的拼接字符串。public class MatchCalculator { private static final MapString, Integer SCORE_MAP new HashMap(); static { // 火象白羊、狮子、射手 String[] fire {白羊座, 狮子座, 射手座}; String[] earth {金牛座, 处女座, 摩羯座}; String[] air {双子座, 天秤座, 水瓶座}; String[] water {巨蟹座, 天蝎座, 双鱼座}; // 同象 90 分相邻象 70 分对冲象 50 分 setGroupScore(fire, 90); setGroupScore(earth, 90); setGroupScore(air, 90); setGroupScore(water, 90); setCrossScore(fire, air, 70); setCrossScore(earth, water, 70); setCrossScore(fire, earth, 50); setCrossScore(air, water, 50); setCrossScore(fire, water, 50); setCrossScore(earth, air, 50); } private static void setGroupScore(String[] group, int score) { for (String a : group) { for (String b : group) { SCORE_MAP.put(a - b, score); } } } private static void setCrossScore(String[] g1, String[] g2, int score) { for (String a : g1) { for (String b : g2) { SCORE_MAP.put(a - b, score); SCORE_MAP.put(b - a, score); } } } public static int calculate(String signA, String signB) { Integer score SCORE_MAP.get(signA - signB); return score ! null ? score : 60; } }static块在类加载时执行一次把 144 种组合全部预计算好运行时calculate只做一次 Map 查询时间复杂度 O(1)。setCrossScore里同时写入a-b和b-a保证查询时不用关心顺序。默认返回 60 分是兜底逻辑防止星座名拼写不一致导致空指针。3.3 从生日到星座日期区间匹配的边界处理用户输入的是生日需要先转成星座。这里最容易翻车的是摩羯座和水瓶座的跨年区间——摩羯座从 12 月 22 日到次年 1 月 19 日如果直接比较字符串会出错。我的做法是把日期转成MM-dd格式后用「大于等于起始日期 且 小于等于结束日期」判断跨年星座单独处理。public static String getSignByDate(String birthday) { // birthday 格式yyyy-MM-dd String md birthday.substring(5); // 取 MM-dd if (md.compareTo(03-21) 0 md.compareTo(04-19) 0) return 白羊座; if (md.compareTo(04-20) 0 md.compareTo(05-20) 0) return 金牛座; if (md.compareTo(05-21) 0 md.compareTo(06-21) 0) return 双子座; if (md.compareTo(06-22) 0 md.compareTo(07-22) 0) return 巨蟹座; if (md.compareTo(07-23) 0 md.compareTo(08-22) 0) return 狮子座; if (md.compareTo(08-23) 0 md.compareTo(09-22) 0) return 处女座; if (md.compareTo(09-23) 0 md.compareTo(10-23) 0) return 天秤座; if (md.compareTo(10-24) 0 md.compareTo(11-22) 0) return 天蝎座; if (md.compareTo(11-23) 0 md.compareTo(12-21) 0) return 射手座; if (md.compareTo(12-22) 0 || md.compareTo(01-19) 0) return 摩羯座; if (md.compareTo(01-20) 0 md.compareTo(02-18) 0) return 水瓶座; return 双鱼座; }substring(5)直接截取MM-dd部分避免解析年份。摩羯座的判断用了||因为它的区间跨年12-22之后或01-19之前都算摩羯。最后返回双鱼座是兜底因为02-19到03-20是唯一剩下的区间。提示如果用户输入的生日格式不固定先用SimpleDateFormat解析再格式化不要直接substring。课设里可以限制输入格式为yyyy-MM-dd减少解析复杂度。4. 界面交互与数据流转从输入到结果展示的完整链路4.1 MainActivity 的输入校验与跳转逻辑MainActivity负责收集用户昵称、性别、生日校验通过后跳转到MatchActivity。输入校验用TextUtils.isEmpty和正则表达式生日用DatePickerDialog选择而不是手动输入减少格式错误。public class MainActivity extends AppCompatActivity { private EditText etName; private TextView tvBirthday; private String selectedGender 男; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etName findViewById(R.id.et_name); tvBirthday findViewById(R.id.tv_birthday); RadioGroup rgGender findViewById(R.id.rg_gender); rgGender.setOnCheckedChangeListener((group, checkedId) - { selectedGender checkedId R.id.rb_male ? 男 : 女; }); tvBirthday.setOnClickListener(v - showDatePicker()); findViewById(R.id.btn_start).setOnClickListener(v - { String name etName.getText().toString().trim(); String birthday tvBirthday.getText().toString().trim(); if (TextUtils.isEmpty(name)) { Toast.makeText(this, 请输入昵称, Toast.LENGTH_SHORT).show(); return; } if (TextUtils.isEmpty(birthday)) { Toast.makeText(this, 请选择生日, Toast.LENGTH_SHORT).show(); return; } Intent intent new Intent(this, MatchActivity.class); intent.putExtra(name, name); intent.putExtra(gender, selectedGender); intent.putExtra(birthday, birthday); startActivity(intent); }); } private void showDatePicker() { Calendar calendar Calendar.getInstance(); new DatePickerDialog(this, (view, year, month, dayOfMonth) - { String date String.format(Locale.CHINA, %04d-%02d-%02d, year, month 1, dayOfMonth); tvBirthday.setText(date); }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)).show(); } }RadioGroup的监听器里直接更新selectedGender比在跳转时再查一遍控件状态更可靠。DatePickerDialog的月份从 0 开始所以month 1不能忘。Intent传参用putExtra逐个传课设数据量小不需要序列化整个对象。4.2 MatchActivity 的结果展示与历史记录写入MatchActivity接收MainActivity传来的数据调用MatchCalculator算出分数展示结果卡片同时把记录写入match_record表。public class MatchActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_match); String name getIntent().getStringExtra(name); String birthday getIntent().getStringExtra(birthday); String mySign MatchCalculator.getSignByDate(birthday); // 随机选一个目标星座做演示实际项目可让用户选择 String[] allSigns {白羊座, 金牛座, 双子座, 巨蟹座, 狮子座, 处女座, 天秤座, 天蝎座, 射手座, 摩羯座, 水瓶座, 双鱼座}; String targetSign allSigns[new Random().nextInt(allSigns.length)]; int score MatchCalculator.calculate(mySign, targetSign); TextView tvResult findViewById(R.id.tv_result); tvResult.setText(String.format(Locale.CHINA, %s你的星座是%s\n与%s的匹配度%d分, name, mySign, targetSign, score)); // 写入历史记录 DBHelper dbHelper new DBHelper(this); SQLiteDatabase db dbHelper.getWritableDatabase(); ContentValues values new ContentValues(); values.put(user_id, 1); values.put(target_sign, targetSign); values.put(score, score); values.put(create_time, new SimpleDateFormat(yyyy-MM-dd HH:mm:ss, Locale.CHINA) .format(new Date())); db.insert(match_record, null, values); db.close(); } }getSignByDate把生日转成星座calculate算出分数ContentValues组装记录后insert进表。user_id这里写死为 1 是简化处理正式项目应该先查user表拿到真实 id。db.close()在 Activity 销毁时调用更合适但课设里写完就关问题不大。4.3 HistoryActivity 用 RecyclerView 展示配对记录历史记录页用RecyclerView展示match_record表的数据按create_time倒序排列。Adapter 里绑定每条记录的星座、分数和时间。public class HistoryActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_history); RecyclerView recyclerView findViewById(R.id.rv_history); recyclerView.setLayoutManager(new LinearLayoutManager(this)); DBHelper dbHelper new DBHelper(this); SQLiteDatabase db dbHelper.getReadableDatabase(); Cursor cursor db.rawQuery( SELECT target_sign, score, create_time FROM match_record ORDER BY create_time DESC, null); ListMatchRecord records new ArrayList(); while (cursor.moveToNext()) { records.add(new MatchRecord( cursor.getString(0), cursor.getInt(1), cursor.getString(2))); } cursor.close(); db.close(); recyclerView.setAdapter(new HistoryAdapter(records)); } }rawQuery直接写 SQL 比query方法更直观课设里不需要考虑 SQL 注入。ORDER BY create_time DESC保证最新的记录在最上面。Cursor用完必须close否则反复进出页面会泄漏。注意如果历史记录页空白先检查match_record表是否有数据再检查RecyclerView的LayoutManager是否设置。最常见的原因是 Adapter 的getItemCount返回了 0而不是数据库没查到。5. 避坑与排查课设开发中最容易翻车的五个点5.1 数据库版本号改了但 onUpgrade 没生效现象给constellation表加了一个字段改了DB_VERSION从 1 到 2重新运行后查询新字段报no such column。原因是onUpgrade里只写了DROP TABLE但没有调用onCreate或者模拟器里旧数据库文件没被删除。解决方法是确保onUpgrade最后调用onCreate(db)或者在测试阶段直接卸载重装 App。更稳妥的做法是在onUpgrade里写ALTER TABLE迁移语句但课设阶段重建表更快。5.2 星座日期判断在跨年时返回错误结果现象用户生日是 1 月 5 日期望返回摩羯座实际返回了水瓶座或双鱼座。原因是日期比较用了yyyy-MM-dd全格式年份不同导致字符串比较失效。解决方法是统一截取MM-dd再比较摩羯座的跨年区间用||单独处理。测试时至少覆盖 12 月 22 日、1 月 19 日、1 月 20 日三个边界日期。5.3 RecyclerView 不显示数据但 Cursor 有记录现象Cursor.getCount()返回 3但列表是空的。原因通常是 Adapter 的onCreateViewHolder里inflate的布局文件根节点设置了match_parent但高度为 0或者getItemCount返回了固定值而不是list.size()。解决方法是检查 Adapter 构造时是否把数据列表赋值给了成员变量以及getItemCount是否返回records.size()。5.4 Android Studio 编译通过但安装到手机闪退现象模拟器上运行正常真机安装后打开就闪退。原因可能是真机 Android 版本低于minSdkVersion或者用了模拟器支持但真机不支持的 API。解决方法是把minSdkVersion调到 24 以下检查build.gradle里是否有vectorDrawables.useSupportLibrary true。另外真机调试时看Logcat的AndroidRuntime标签崩溃原因一目了然。5.5 论文里的「详细设计」写成了代码堆砌现象论文详细设计章节贴了五百行代码答辩老师问「你的设计思路是什么」答不上来。原因是把代码当设计缺少架构图和流程说明。解决方法是详细设计章节按「模块划分 → 类图 → 关键流程时序 → 核心算法说明」四段写代码只贴关键片段并配文字解释。MVC 三层架构的包图、配对算法的流程图、数据库 ER 图这三张图放进去论文字数和质量都够了。6. 从课设到答辩论文写作节奏与演示脚本的打磨技巧论文写到八千字左右最容易出现的问题是「前面太胖后面太瘦」——项目背景写了三千字详细设计只有一千字。我的经验是按 2:3:3:2 分配背景和意义占两成开发环境和相关技术占三成详细设计与实现占三成运行演示和心得体会占两成。开发环境那部分不要只写「安装了 Android Studio」要把 SDK 版本、Gradle 版本、数据库版本、测试设备型号都列出来这些细节能让老师觉得你真的动手了。运行演示章节配截图时每张图下面写三到五行说明讲清楚「这个界面在做什么、用户怎么操作、数据从哪来到哪去」。比如配对结果页的截图说明里要提到「分数由 MatchCalculator 根据星座矩阵计算得出同时写入 match_record 表供历史页查询」。这样截图就不是装饰而是设计的一部分。答辩演示脚本我一般准备三个场景第一次配对、查看历史记录、删除一条记录。每个场景控制在 40 秒以内操作路径提前走熟不要现场找按钮。老师常问的问题集中在「为什么用 SQLite 不用 MySQL」「MVC 和 MVP 的区别」「配对算法的依据是什么」前两个问题提前准备好标准答案第三个问题就如实说「参考了常见的星座配对表用同象高分、对冲低分的规则实现」。最后一个技巧是关于心得体会的写法。不要写「通过这次课设我学到了很多」而是写一个具体的翻车经历比如「星座日期跨年判断一开始用字符串全比较导致 1 月 5 日被识别成水瓶座后来改成截取 MM-dd 并单独处理摩羯座区间才解决」。这种细节写进去老师一看就知道是你自己踩过的坑比空泛的感悟有说服力得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表