尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于 Android 的博物馆文化展示与预约 App —— 设计与实现(有源码和文档)

基于 Android 的博物馆文化展示与预约 App —— 设计与实现(有源码和文档) 一、项目背景与目标随着让文物活起来和数字博物馆理念的普及越来越多的博物馆开始借助移动应用向公众提供藏品展示、新闻资讯、参观预约等数字化服务。本文介绍的museumApp是一款以博物馆文化展示与预约为核心的 Android 原生应用其目标是集中展示在一个 App 内集成博物馆简介、开放时间、新闻资讯、展览活动、藏品典藏等内容。在线预约让观众可以填写预约日期、人数、时段提交到本地数据库并在我的中查看自己的预约记录。个人中心支持账号注册、登录、个人资料维护含头像更换并区分普通用户与管理员角色。教学价值作为 Android 课程设计的实践项目对启动页 → 登录/注册 → 底部导航 Fragment → SQLite 持久化 → 详情页这一典型 App 流程进行完整封装。特别说明项目虽然引入了volley.jar网络库但实际业务中并未发起任何真实网络请求——所有藏品、新闻、活动、用户、预约等数据均通过本地 SQLite 或硬编码资源R.drawable提供属于离线优先的教学型 Demo可以保证课堂演示时不依赖外网。二、整体架构App 采用经典的MVC 单 Activity 多 Fragment结构UI 层 Activity / Fragment / Adapter (View) 业务层 DBUser / DBReservation基于 SQLiteOpenHelper 的 DAO 数据层 entityUser / Reservation / News / Blank SPUtils 资源层 drawable / layout / values一个MainActivity承载底部BottomNavigationView通过FragmentTransaction.replace()切换首页 / 典藏 / 我的三个 Fragment。业务页藏品、活动、新闻、预约、详情等各自独立Activity跳转通过Intent完成。数据持久化由DBHelper单例的SQLiteOpenHelper 两个 DAODBUser、DBReservation承担登录态、自动登录、记住密码等小数据由SPUtilsSharedPreferences 封装存储。三、技术栈与依赖build.gradle中声明的依赖技术用途AppCompat / Material / ConstraintLayout基础 UI 框架与主题RecyclerView列表控件项目中以 ListView 为主RecyclerView 备用ViewPager 自定义AdPagerAdapter首页顶部 3 秒自动轮播 触摸暂停/继续Glide glide-transformations头像与详情大图的加载与变换PictureSelector (picture_libraryv2.6.0)我的资料中更换头像相册/拍照volley.jarlibs 目录已引入但未使用SQLite系统内置用户表、预约表// build.gradle 节选 implementation androidx.appcompat:appcompat:1.3.0 implementation com.google.android.material:material:1.4.0 implementation androidx.constraintlayout:constraintlayout:2.0.4 implementation com.github.bumptech.glide:glide:4.11.0 implementation com.github.LuckSiege.PictureSelector:picture_library:v2.6.0 implementation jp.wasabeef:glide-transformations:3.3.0 implementation files(libs\\volley.jar)四、目录结构与 Java 文件清单graduate/museumApp/ ├── build.gradle ├── libs/volley.jar └── src/main/ ├── AndroidManifest.xml ├── res/ drawable / layout / values 等 └── java/com/example/museum/ ├── activity/ 13 个 Activity ├── fragment/ 3 个 Fragment ├── adapter/ 5 个 Adapter ├── database/ 3 个数据库类 ├── entity/ 4 个实体 └── utils/ 3 个工具类各包关键类说明包关键类职责activityOpenActivity启动页5 秒倒计时 数据库初始化 跳转登录/主页LoginActivity登录账号、密码、记住密码、自动登录RegisterActivity注册昵称/性别/手机号/职业/兴趣/密码/头像MainActivity主页底部导航 Fragment 容器ActivitiesActivity教育活动列表NewsActivity新闻列表CollectionActivity典藏列表按分类ReservationActivity网上预约表单MyInfoActivity我的个人资料编辑 头像MyRecordActivity我的预约记录DetailActivity通用详情博物馆简介/开放时间CollectionDetailActivity藏品详情NewsDetailActivity新闻详情fragmentBlankFragment_Main首页轮播 功能入口BlankFragment_Collection典藏分类 FragmentBlankFragment_me“我的” FragmentadapterAdPagerAdapter首页轮播 ViewPager 适配器BlankAdapter通用卡片 ArrayAdapter首页功能入口NewsAdapter新闻列表CollectionAdapter典藏分类RecordAdapter预约记录databaseDBHelperSQLiteOpenHelper 单例建表 初始化管理员DBUser用户增删改查、登录校验DBReservation预约新增 / 查询按用户名筛选entityUser/Reservation/News/Blank4 个 POJOutilsSPUtilsSharedPreferences 封装键值常量化GlideEngine实现 PictureSelector 的ImageEngineUserTypeEnum用户类型枚举普通用户/管理员/院长五、数据库设计数据库名museum.db版本 1存在应用私有数据目录下。5.1 用户表users字段类型约束说明nametextPRIMARY KEY用户名账号pswtextNOT NULL密码occupationtextNOT NULL职业sextextNOT NULL性别phonetextNOT NULL手机号interesttextNOT NULL兴趣phototextNOT NULL头像路径userTypetextNOT NULL用户类型普通/管理员/院长5.2 预约表reservation字段类型约束说明idintegerPRIMARY KEY AUTOINCREMENT预约 IDnametextNOT NULL预约人姓名timetextNOT NULL预约日期numbertextNOT NULL预约人数ampmtextNOT NULL上午/下午首次启动时DBHelper.init()会判断管理员账号admin / 123是否存在若不存在则写入并把当前登录用户缓存到DBUser.user静态字段中便于全局访问。核心建表代码Override public void onCreate(SQLiteDatabase sqLiteDatabase) { String sqlcreate table users(name text primary key,psw text not null, occupation text not null,sex text not null,phone text not null, interest text not null,photo text not null,userType text not null); sqLiteDatabase.execSQL(sql); sql CREATE TABLE reservation (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, time TEXT NOT NULL, number TEXT NOT NULL, ampm TEXT NOT NULL); sqLiteDatabase.execSQL(sql); }六、关键模块实现6.1 启动页与登录态判断OpenActivity是 LAUNCHER 入口使用CountDownTimer实现 5 秒倒计时与跳过按钮public class OpenActivity extends AppCompatActivity { Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_open); DBHelper.GetInstance(this.getApplication()).init(); in findViewById(R.id.in); // 倒计时 5 秒 CountDownTimer timer new CountDownTimer(5000, 1000) { public void onTick(long millisUntilFinished) { in.setText(millisUntilFinished / 1000 秒); } public void onFinish() { finishView(); } }; timer.start(); } private void finishView(){ Intent intent new Intent(); // 根据 SPUtils 中的登录态决定去向 if (!TextUtils.isEmpty(SPUtils.getName(this)) (Boolean) SPUtils.get(this, SPUtils.AUTO_LOGIN, false)) { intent.setClass(OpenActivity.this, MainActivity.class); } else { intent.setClass(OpenActivity.this, LoginActivity.class); } startActivity(intent); finish(); } }登录页支持自动登录和记住密码两个选项二者状态均由SPUtils持久化。校验逻辑直接走DBUser.check(account, password)从本地users表中匹配。6.2 主页与底部导航MainActivity是 App 唯一的框架页由BottomNavigationViewFrameLayout组合通过FragmentTransaction.replace()切换 3 个 Fragmentpublic class MainActivity extends AppCompatActivity { FrameLayout main; BottomNavigationView navigation; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); main findViewById(R.id.main); navigation findViewById(R.id.navigation); navigation.setOnItemSelectedListener(item - { switch (item.getItemId()) { case R.id.home: replace(new BlankFragment_Main().newInstance()); return true; case R.id.cart: replace(new BlankFragment_Collection().newInstance()); return true; case R.id.me: replace(new BlankFragment_me().newInstance()); return true; } return false; }); navigation.setSelectedItemId(R.id.home); // 默认首页 } }底部三 Tab 分别为首页 / 典藏 / 我的。6.3 首页自动轮播 功能入口BlankFragment_Main把广告位和功能入口列表组合在一起顶部ViewPager加载 4 张本地drawablehead1~head4用Handler.postDelayed实现 3 秒自动切换触摸时通过OnTouchListener暂停轮播抬手 / 取消时延 1 秒后继续下方ListView加载 5 个Blank实体博物馆简介、开放时间、教育活动、网上预约、新闻咨询点击不同位置分别跳到DetailActivity/ActivitiesActivity/ReservationActivity/NewsActivity。private ViewPager mViewPager; private Handler mHandler new Handler(); private Runnable mRunnable new Runnable() { Override public void run() { int currentItem mViewPager.getCurrentItem(); int nextItem (currentItem 1) % mViewPager.getAdapter().getCount(); mViewPager.setCurrentItem(nextItem); mHandler.postDelayed(this, 3000); } }; int[] adImages {R.drawable.head4, R.drawable.head2, R.drawable.head3, R.drawable.head1}; AdPagerAdapter adapter new AdPagerAdapter(getActivity(), adImages); mViewPager.setOnTouchListener((v, event) - { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mHandler.removeCallbacks(mRunnable); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mHandler.postDelayed(mRunnable, 1000); break; } return false; });6.4 典藏分类与详情BlankFragment_Collection进入典藏Tab 时展示 5 个分类入口镇馆之宝 / 精品鉴赏 / 三维藏品 / 藏品公开 / 藏品征集。每条点击进入CollectionActivity再以CollectionAdapter渲染同分类下的具体藏品缩略图 名称点入CollectionDetailActivity查看大图与文字详情。6.5 新闻与活动NewsActivity与ActivitiesActivity使用ArrayAdapterNews渲染图文列表点击进入NewsDetailActivity/DetailActivity渲染正文。这部分数据与首页一样来自代码内硬编码未做联网。6.6 网上预约ReservationActivity是 App 业务核心通过DatePickerDialog选择预约日期自动阻止选择过去日期并格式化为yyyy年MM月dd日使用RadioGroup选择上午 / 下午表单上展示当前时段剩余预约量DBReservation.allCount - DBReservation.count其中allCount 999提交时校验人数不能为空、本次人数 历史累计不能超过 999校验通过后调用DBReservation.add(...)写入数据库。protected void showDatePickerDialog() { new DatePickerDialog(this, (view, yearSet, monthSet, daySet) - { if (yearSet year) { Toast.makeText(this, 不能选择过去日期, Toast.LENGTH_SHORT).show(); return; } Calendar c Calendar.getInstance(); c.set(yearSet, monthSet, daySet); time new SimpleDateFormat(yyyy年MM月dd日, Locale.CHINA).format(c.getTime()); textView.setText(time); }, year, month - 1, day).show(); } private void save() { String number edit_number.getText().toString(); String am rgSex.getCheckedRadioButtonId() R.id.rb_am ? 上午 : 下午; if (.equals(number)) { Toast.makeText(this, 人数不能为空, Toast.LENGTH_LONG).show(); return; } if (DBReservation.count Integer.valueOf(number) 999) { Toast.makeText(this, 输入人数超出剩余数量, Toast.LENGTH_LONG).show(); return; } DBReservation db new DBReservation(getBaseContext()); db.add(new Reservation(DBUser.user.getName(), time, number, am)); Toast.makeText(this, 保存成功, Toast.LENGTH_SHORT).show(); finish(); }预约完成后可在我的 → 预约记录中按用户名筛选展示历史预约。6.7 个人中心我的Fragment 通过DBUser.user静态字段拿到当前登录用户渲染头像 昵称 三个入口个人信息→MyInfoActivity支持修改昵称/性别/手机号/职业/兴趣并通过PictureSelectorGlideEngine拍照/选图更换头像写入users.photo。预约记录→MyRecordActivity调用DBReservation.getAll(userName)拿到当前用户所有预约。退出登录清空SPUtils后回到LoginActivity。七、关键流程7.1 启动与登录流程OpenActivity启动页5s 倒计时 │ ├── DBHelper.init()建库 写入管理员 admin/123 │ ├── SPUtils.AUTO_LOGIN true → MainActivity │ └── 否则 → LoginActivity ├── 点击注册 → RegisterActivity → LoginActivity └── 登录成功 → MainActivity7.2 预约流程MainActivity → 首页网上预约卡片 → ReservationActivity → 选择日期 上午/下午 人数 → 校验通过 → DBReservation.add() → 跳回 MainActivity我的 → 预约记录 → MyRecordActivity 拉取记录八、运行效果下面整理项目pic/目录下的实际运行截图按典型用户路径排序阶段截图说明启动后首页pic/Screenshot_20240630_085930.png顶部 ViewPager 轮播 5 个功能入口 底部三 Tab进入功能pic/Screenshot_20240630_090022.png点击博物馆简介进入通用详情页活动列表pic/Screenshot_20240630_090157.png教育活动图文列表活动详情pic/Screenshot_20240630_090214.png大图 活动正文典藏分类pic/Screenshot_20240630_090606.png5 个圆角按钮典藏列表pic/Screenshot_20240630_090625.png镇馆之宝典藏列表pic/Screenshot_20240630_090642.png精品鉴赏典藏列表pic/Screenshot_20240630_090707.png三维藏品典藏列表pic/Screenshot_20240630_090728.png藏品公开典藏列表pic/Screenshot_20240630_090750.png藏品征集典藏详情pic/Screenshot_20240630_090803.png大图 详情正文新闻列表pic/Screenshot_20240630_090254.png招聘、活动、新闻混合新闻详情pic/Screenshot_20240630_090312.png时间 长文登录pic/Screenshot_20240630_124422.png记住密码 / 自动登录注册pic/Screenshot_20240630_124439.png7 个字段 头像我的pic/Screenshot_20240630_124356.png头像 三个入口个人资料pic/Screenshot_20240630_124509.png头像 表单 保存预约记录pic/Screenshot_20240630_124550.png按用户筛选后的记录九、设计亮点与经验总结单 Activity Fragment 的轻量主页模式业务页各自独立Activity主页仅保留首页 / 典藏 / 我的三个 Fragment避免传统多 Activity Activity 间互相回传的样板代码。DAO 单例DBHelperDBHelper.GetInstance(context)全程复用SQLiteDatabase避免频繁open/close同时把onCreate写表与init()初始化种子数据解耦。首页轮播的触摸可控 自动续播通过OnTouchListener监听ACTION_DOWN/UP/CANCEL再配合Handler.removeCallbacks/postDelayed既能暂停也能延时恢复体验比第三方 Banner 控件更可控。预约余量的软上限allCount 999、count跟随getAll()累加是一种教学型防超——既演示了 DAO 的统计查询又避免了对真实库存逻辑的过度设计。登录态分离把是否记住密码 / 是否自动登录两个状态拆为两个CheckBox 两个SPUtils键值是常见需求再叠加CountDownTimer启动页倒计时完整覆盖真实 App 的冷启动 → 拉起主页的体验链路。可扩展的通用详情页DetailActivity接收Blank实体作为Intent附加数据由Blank携带图标 标题 描述使博物馆简介 / 开放时间等纯展示型页面共用一个 Activity降低模板代码。十、可改进点虽然本项目足以支撑教学演示和功能跑通但仍有不少可演进空间真实网络层把硬编码列表数据抽到接口/api/collections、/api/news用 Retrofit/OkHttp 替换当前的volley.jar占位并在ListView上加SwipeRefreshLayout。Room 替代裸 SQLite用 Room 把User/Reservation注解为 Entity写编译期校验的 DAO 接口配合LiveData/Flow让预约记录自动刷新。MVVM 化引入ViewModel LiveData把首页 Fragment 业务 Activity改造成数据驱动配合ViewBinding替换findViewById。图片选择统一化用ActivityResultContracts替代旧版onActivityResult并结合 Android 13 的READ_MEDIA_IMAGES权限模型重构头像选择。暗色模式与国际化当前主题写死绿色#1AB394可结合values-night与Material You提升一致性。统一包名遗留AndroidManifest.xml的packagecom.example.smallshop与 Java 包com.example.museum不一致可通过android.namespaceapplicationIdSuffix整理避免R类的 import 必须写成com.example.smallshop.R。十一、小结museumApp 用最经典的启动页 登录 主页三 Tab 详情/表单页结构完整跑通了一个 Android 入门到进阶的博物馆文化展示与预约业务。它不追求花哨的视觉效果但把Activity/Fragment 导航、SQLite 持久化、SharedPreferences 登录态、ViewPager 轮播、PictureSelector 选图、Glide 加载等基础但重要的知识点全部串了起来非常适合作为 Android 课程设计或入门实战项目的参考实现。
返回列表