☰
Android毕设简易App实战:导航、设置与真机兼容方案
2026/11/3 11:42:42 网站建设 项目流程

简介:本资源是一份面向Android开发初学者的实战入门指南,聚焦使用Android Studio 4.x及Java语言构建基础功能App,适用于高校移动应用开发课程实践、自学入门者快速上手和求职前项目练手。文档以图文结合方式详解三页面简易App的完整开发流程:从新建Empty Activity项目、配置最低SDK(Android 4.0)、创建MainActivity/AboutActivity/SettingsActivity三大核心Activity,到编写onCreate方法加载布局、注册Activity至AndroidManifest.xml,再到设置页面集成PreferenceFragmentCompat实现偏好管理。资源为单文件PDF格式,共1个498KB的便携式技术文档,内容精炼、代码可直接参考,含关键类结构、XML布局调用逻辑与Intent跳转提示。目前已有447人学习下载,适合零基础读者按步骤复现项目结构、理解Android四大组件基础协作关系,并建立标准工程目录认知。

1. 这不是“Hello World”:一个能过答辩、能装进真机、能被导师点开就看懂的 Android 毕设级简易 App 方案

你手头那份标着“android+studio简易app实例.pdf”的文档,大概率是某位学长导出的笔记截图,或是某次课设压缩包里没删干净的 PDF。它列了 11 步,但没告诉你:为什么选 Empty Activity 而不是 BottomNavigationView Activity?为什么最低版本敢设到 Android 4.0(API 14)——这在 2024 年连模拟器都懒得启动;更关键的是,它把onCreate()代码贴出来就收工,却没说明R.layout.activity_main这个资源 ID 是怎么生成的、XML 布局里缺了tools:context会导致预览器一片红、AndroidManifest.xml里<category android:name="android.intent.category.INFO"/>实际上根本不会出现在系统设置页——这些不是细节,是答辩时被问住的起点。
这份方案,就是为解决这类「看着会、一做就卡、一跑就崩、一讲就虚」的真实毕设/课设场景而写的。它不教你怎么点菜单,而是带你亲手搭起一个有导航逻辑、有状态保存、有可扩展结构、能通过 Gradle 构建、能在 Android 8.0 到 14.0 真机上稳定运行的最小可行 App。适合正在赶 Android 课程设计 deadline 的同学,也适合需要快速交付一个演示原型的初级工程师——它不炫技,但每一步都经得起追问。

2. 总体架构设计:三层结构与四个核心模块的职责划分

这个简易 App 表面只有三个页面,但若按工程化思路组织,必须明确分层边界。我通常采用「Activity 层 + Fragment 层 + Resource 层」三层结构,避免所有逻辑堆在MainActivity里变成黑匣子。四个核心模块分别是:启动入口模块(Launcher)、主功能容器模块(Home Container)、信息展示模块(About)、偏好配置模块(Settings)。它们之间不直接耦合,全部通过 Intent 显式跳转,且每个模块只负责自己范围内的 UI 渲染与轻量交互。

2.1 启动入口模块:从AndroidManifest.xml开始的第一次校验

很多同学在AndroidManifest.xml里随手写<action android:name=".MainActivity" />,结果发现图标不显示、应用打不开。根本原因是:Launcher Activity 的 intent-filter 必须同时满足 action 为ACTION_MAIN、category 为CATEGORY_LAUNCHER,且 action 名称不能自定义。正确写法如下:

<activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>

提示:android:exported="true"是 Android 12(API 31)强制要求,漏写会导致安装失败。若目标 SDK ≥ 31,所有含 intent-filter 的 Activity 都必须显式声明 exported 属性。

2.2 主功能容器模块:用ConstraintLayout承载可复用的导航按钮组

主页面不是空白画布,而是导航中枢。我坚持用ConstraintLayout(而非LinearLayout或RelativeLayout)作为根布局,原因很实际:它支持链式约束,能让「关于」「设置」两个按钮在不同屏幕尺寸下保持等宽居中,且 XML 结构扁平无嵌套。activity_main.xml关键片段如下:

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/btn_about" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="32dp" android:layout_marginEnd="16dp" android:text="@string/title_about" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/btn_settings" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.4" /> <Button android:id="@+id/btn_settings" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="32dp" android:text="@string/title_settings" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/btn_about" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.4" /> </androidx.constraintlayout.widget.ConstraintLayout>

注意app:layout_constraintVertical_bias="0.4"—— 这比硬写android:layout_marginTop="200dp"更健壮,它让按钮始终位于屏幕垂直方向 40% 处,适配全面屏和折叠屏。

2.3 信息展示模块:AboutActivity的静态内容封装与版本自动注入

AboutActivity不该只是展示固定文字。我习惯在strings.xml中定义占位符,并在onCreate()中动态注入当前 App 版本号,让信息具备可信度:

<!-- res/values/strings.xml --> <string name="about_version">版本:%1$s</string> <string name="about_copyright">© %1$d %2$s</string>
// AboutActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_about); TextView versionText = findViewById(R.id.text_version); TextView copyrightText = findViewById(R.id.text_copyright); try { PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); String versionName = pInfo.versionName; int versionCode = pInfo.versionCode; int currentYear = Calendar.getInstance().get(Calendar.YEAR); String appName = getString(R.string.app_name); versionText.setText(getString(R.string.about_version, versionName)); copyrightText.setText(getString(R.string.about_copyright, currentYear, appName)); } catch (PackageManager.NameNotFoundException e) { versionText.setText("版本:未知"); copyrightText.setText("© 2024 应用"); } }

这样,哪怕你改了build.gradle里的versionName,About 页面也会自动同步,不用每次手动改字符串。

2.4 偏好配置模块:PreferenceFragmentCompat的现代用法与兼容性兜底

SettingsActivity是最容易翻车的模块。原文档里混用了PreferenceFragment和PreferenceFragmentCompat,而后者才是 AndroidX 推荐的兼容方案。关键在于:SettingsFragment必须继承PreferenceFragmentCompat,且SettingsActivity的布局必须包含一个FrameLayout容器用于动态加载 Fragment:

<!-- activity_settings.xml --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/settings_container" android:layout_width="match_parent" android:layout_height="match_parent" />
// SettingsFragment.java public class SettingsFragment extends PreferenceFragmentCompat { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 注意:setPreferencesFromResource 的第一个参数是 R.xml.xxx,不是 R.layout.xxx setPreferencesFromResource(R.xml.preferences, null); } }

R.xml.preferences是一个标准 preference XML 文件,定义了 ListPreference、SwitchPreferenceCompat 等控件。这种分离方式让设置逻辑完全脱离 Activity,便于单元测试和未来迁移到 Jetpack Compose。

3. 核心功能实现:从按钮跳转到偏好持久化的完整链路

光有页面骨架不够,用户点击「关于」后得真跳过去,改了主题色得下次打开还在。这一章把「导航」和「数据保存」两条主线串起来,给出可直接复制粘贴的代码,并解释每一行为什么这么写。

3.1 导航跳转:Intent显式调用的三要素与防错处理

MainActivity中按钮的onClick方法不能只写startActivity(new Intent(this, AboutActivity.class))。必须补全三要素:Context、目标 Class、异常捕获。否则在某些定制 ROM 上可能因 Activity 未注册而崩溃:

public void onAboutButtonClick(View view) { Intent intent = new Intent(MainActivity.this, AboutActivity.class); // 添加 FLAG_ACTIVITY_CLEAR_TOP 防止多次点击产生多个 AboutActivity 实例 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 启动前校验目标 Activity 是否存在(防御性编程) if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } else { Toast.makeText(this, "关于页面暂不可用", Toast.LENGTH_SHORT).show(); } }

注意:FLAG_ACTIVITY_CLEAR_TOP是血泪经验。曾有个学生做毕设,用户狂点「关于」按钮,退出时按返回键要连按 5 次才能回到桌面——就是因为没清栈。

3.2 偏好数据读写:SharedPreferences的线程安全封装与实时监听

设置页面的开关、列表选择项,最终都要存到SharedPreferences。但直接用getSharedPreferences().edit().putString().apply()容易出错:apply()是异步的,若紧接着读取可能拿到旧值;commit()又阻塞主线程。我的做法是封装一个工具类PrefUtils,统一管理:

// PrefUtils.java public class PrefUtils { private static final String PREF_NAME = "app_settings"; private static SharedPreferences sPrefs; public static void init(Context context) { sPrefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); } public static void putString(String key, String value) { sPrefs.edit().putString(key, value).apply(); // apply() 在这里安全,因无后续立即读取 } public static String getString(String key, String defValue) { return sPrefs.getString(key, defValue); } // 提供监听器,当设置变更时自动回调(如主题切换需刷新 UI) public static void registerOnSharedPreferenceChangeListener( SharedPreferences.OnSharedPreferenceChangeListener listener) { sPrefs.registerOnSharedPreferenceChangeListener(listener); } }

在SettingsFragment的onCreate()中初始化监听:

@Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setPreferencesFromResource(R.xml.preferences, null); // 监听主题设置变化 findPreference("theme_preference").setOnPreferenceChangeListener( (preference, newValue) -> { // 这里可触发 UI 重绘,或发广播通知 MainActivity requireActivity().recreate(); // 简单粗暴,适合毕设 return true; }); }

3.3 字符串资源与多语言支持:strings.xml的结构化组织技巧

别把所有字符串塞进一个strings.xml。我按模块拆分为:

  • strings.xml(默认,中文)
  • strings-en.xml(英文,用于国际化演示)
  • arrays.xml(存放 ListPreference 的 entries/entryValues)

例如arrays.xml:

<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="font_size_entries"> <item>小</item> <item>中</item> <item>大</item> </string-array> <string-array name="font_size_values"> <item>12</item> <item>16</item> <item>20</item> </string-array> </resources>

然后在preferences.xml中引用:

<ListPreference android:key="font_size_preference" android:title="字体大小" android:entries="@array/font_size_entries" android:entryValues="@array/font_size_values" android:defaultValue="16" />

这样,即使不做多语言,结构也清晰,导师一眼看出你懂资源管理。

3.4 构建配置:build.gradle(Module: app)的关键参数设定

Gradle 配置决定项目能否编译通过、能否在新设备运行。以下是毕设级最简但完备的配置(基于 Android Studio Giraffe 2022.3.1 + AGP 8.1.0):

android { namespace 'com.example.simpleapp' compileSdk 34 defaultConfig { applicationId "com.example.simpleapp" minSdk 19 // 改为 19,放弃 Android 4.0(API 14),兼容性与开发效率平衡点 targetSdk 34 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.10.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.preference:preference:1.2.1' implementation 'androidx.fragment:fragment:1.6.2' }

提示:minSdk 19(Android 4.4)是当前最低合理值。API 14 已淘汰近十年,连 Google Play Console 都不再接受 API < 19 的新应用上传。

4. 常见问题排查:毕设中最容易踩的 4 个坑及现场急救方案

这些问题我在带三个不同高校的 Android 课设时反复遇到,学生常卡在最后一步,调试两小时找不到原因。以下按「现象 → 原因 → 解决」给出可立即执行的方案。

4.1 现象:App 安装后桌面无图标,点击 Run 按钮报错Installation did not succeed

原因:AndroidManifest.xml中 Launcher Activity 缺少android:exported="true"(Android 12+ 强制要求),或intent-filter写错(如 action 名称不是android.intent.action.MAIN)。
解决:

  1. 检查AndroidManifest.xml中 Launcher Activity 的<intent-filter>是否严格匹配:
    <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
  2. 确认该 Activity 声明了android:exported="true";
  3. 清理项目:Build → Clean Project,再Build → Rebuild Project;
  4. 卸载设备上残留的同包名旧版本(adb uninstall com.example.simpleapp)。

4.2 现象:点击「关于」按钮后黑屏或闪退,Logcat 报ActivityNotFoundException

原因:AboutActivity未在AndroidManifest.xml中声明,或声明的android:name与 Java 类名不一致(如大小写错误、缺少包名前缀)。
解决:

  1. 打开AndroidManifest.xml,确认存在:
    <activity android:name=".AboutActivity" android:exported="false" />
  2. 检查AboutActivity.java的包声明是否为package com.example.simpleapp;(与applicationId一致);
  3. 在MainActivity的onAboutButtonClick中,用Log.d("DEBUG", "AboutActivity class: " + AboutActivity.class.getName());打印类名,确认路径正确。

4.3 现象:设置页面打开后空白,Logcat 显示No view found for id ...

原因:SettingsActivity的布局文件activity_settings.xml中没有 ID 为settings_container的容器,或SettingsFragment的setPreferencesFromResource()加载了错误的 XML(如误用R.layout.xxx而非R.xml.xxx)。
解决:

  1. 检查activity_settings.xml是否包含:
    <FrameLayout android:id="@+id/settings_container" ... />
  2. 检查SettingsFragment.java中setPreferencesFromResource(R.xml.preferences, null)的第一个参数是否为R.xml.xxx(XML 文件必须放在res/xml/目录下);
  3. 确认res/xml/preferences.xml文件存在且格式正确(根节点为<PreferenceScreen>)。

4.4 现象:修改设置后重启 App,选项恢复默认值

原因:SharedPreferences的 key 写错(如putString("theme", ...)但读取时用getString("theme_preference", ...)),或ListPreference的defaultValue未在 XML 中设置。
解决:

  1. 统一检查所有Preference的android:key属性,确保与PrefUtils.putString()中的 key 完全一致;
  2. 在preferences.xml中为每个 Preference 设置android:defaultValue,例如:
    <SwitchPreferenceCompat android:key="dark_mode" android:title="深色模式" android:defaultValue="false" />
  3. 使用 Device File Explorer 查看/data/data/com.example.simpleapp/shared_prefs/app_settings.xml,确认值已写入。

5. 答辩与验收:用一份「可演示、可讲解、可验证」的交付物打动导师

答辩不是背诵步骤,而是证明你真正掌控了这个 App。我建议准备三样东西:一个 3 分钟演示视频、一份精简版技术说明文档(PDF)、以及一个「答辩问答速查表」。下面说具体怎么做。

5.1 演示视频脚本:聚焦「用户旅程」而非「开发过程」

不要录「我点 File → New Project → …」,导师不关心你点了几下菜单。要录真实用户会做的操作:

  • 开场(0:00–0:20):解锁手机,找到 App 图标,点击启动;
  • 主流程(0:20–1:50):点击「关于」→ 展示版本号与版权信息 → 返回 → 点击「设置」→ 切换「深色模式」开关 → 点击「字体大小」→ 选择「大」→ 返回主页面,观察按钮文字变大(若做了适配);
  • 压轴(1:50–3:00):卸载 App → 重新安装 → 打开设置,验证「深色模式」和「字体大小」仍为上次选择的值(证明SharedPreferences持久化生效)。
    视频用手机录屏即可,导出为 MP4,命名simpleapp_demo_2024.mp4。答辩时直接播放,比你口头描述十遍都管用。

5.2 技术说明文档:一页纸讲清架构与关键决策

这份 PDF 就是你交作业的「说明书」,控制在一页 A4 内。我用 Markdown 写好,用 Typora 导出 PDF。结构如下:

项目说明
架构图文字描述:「Activity 层(MainActivity/AboutActivity/SettingsActivity)仅负责页面跳转与生命周期;Fragment 层(SettingsFragment)承载设置逻辑;Resource 层(strings.xml / xml/preferences.xml)管理文本与配置」
技术选型理由「选用 Java 而非 Kotlin:课程要求统一语言;选用PreferenceFragmentCompat:兼容 Android 5.0+,避免PreferenceActivity过时;minSdk=19:放弃 Android 4.x,提升开发效率与安全性」
核心功能验证点「1. 三个页面可通过按钮正常跳转;2. 设置项修改后重启 App 仍保留;3. 在 Android 8.0(API 26)、11(API 30)、14(API 34)真机上均能安装并运行」
文件清单「共 12 个关键文件:MainActivity.java,AboutActivity.java,SettingsActivity.java,SettingsFragment.java,activity_main.xml,activity_about.xml,activity_settings.xml,preferences.xml,strings.xml,arrays.xml,AndroidManifest.xml,build.gradle(Module: app)」

5.3 答辩问答速查表:预判导师最可能问的 5 个问题

把这些问题写在一张便签纸上,答辩前默读三遍:

  1. Q:为什么不用 Navigation Component?
    A:Navigation Component 适合复杂单 Activity 架构,本项目为典型多 Activity 模式,用 Intent 显式跳转更直观,符合课程教学目标。

  2. Q:SharedPreferences存敏感数据安全吗?
    A:本项目仅存储 UI 偏好(字体、主题),不涉及密码、Token 等敏感信息。若需存敏感数据,应使用EncryptedSharedPreferences(AndroidX Security 库)。

  3. Q:如何扩展「关于」页面加入开发者邮箱?
    A:在strings.xml新增<string name="about_email">dev@simpleapp.com</string>,在activity_about.xml中加TextView,并在AboutActivity.java的onCreate()中findViewById().setText(getString(R.string.about_email))。

  4. Q:如果想加「退出」按钮,怎么实现?
    A:在MainActivity布局中加 Button,onClick方法内调用finishAffinity()(关闭所有 Activity)+System.exit(0)(彻底退出进程),但需提醒用户:Android 不鼓励强制退出,更推荐按返回键自然退出。

  5. Q:这个 App 能发布到应用商店吗?
    A:可以,但需补充:应用图标(mipmap)、隐私政策链接(在About页面添加)、签名 APK(Build → Generate Signed Bundle/APK),以及 Google Play 要求的targetSdk≥ 33(当前为 34,已满足)。

从那以后我每次带学生做 Android 课设,都会强制他们在git commit前,先跑一遍「演示视频脚本」里的三步操作:启动 → 设置 → 重启验证。只要这三步通,答辩基本稳了。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询