Android 宠物健康服务平台实战:从架构设计到主题换肤完整解析(源码和文档)

摘要:本文基于一个完整的 Android 毕业设计项目,介绍如何构建一款集宠物健康档案、在线商城、宠物社区、订单管理、主题换肤于一体的宠物服务 App。文章结合真实运行截图与核心代码,讲解 ViewPager + Fragment 底部导航、SQLite 本地持久化、OkHttp 网络请求、skin-support 动态换肤等关键技术,适合 Android 初学者及毕设参考。

关键词:Android;宠物健康;SQLite;OkHttp;Fragment;主题换肤;毕业设计


一、项目背景与功能概述

随着养宠人群的增长,围绕宠物的健康管理、社交分享和周边消费逐渐形成一体化需求。本项目是一款面向 Android 端的宠物健康综合服务平台,核心定位是:

  • 宠物健康档案:记录宠物年龄、体重、疫苗、洗澡日期等信息;
  • 在线商城:猫粮、狗粮等宠物用品浏览、搜索、加购、下单;
  • 宠物社区:用户可发布图文、点赞、评论,形成互动;
  • 订单管理:个人购买记录与管理员订单视图;
  • 主题换肤:支持默认绿、酷黑、梦幻紫三套主题动态切换。

项目采用 Java 开发,源码包为 com.example.order,应用 ID 为 com.example.petHealth,编译 SDK 30,最低支持 Android 5.0(API 21)。


二、技术栈与架构设计

2.1 技术栈

技术/框架 说明
Android SDK 30 编译与构建环境
Java 1.8 开发语言
BottomNavigationView + Fragment 底部导航与页面切换
ViewPager 商城首页广告轮播
RecyclerView / ListView 商品、社区、评论等列表展示
SQLite + 7 张表 本地数据持久化
OkHttp 4.4.0 异步网络请求
Gson 2.8.6 JSON 序列化与反序列化
Glide 4.11.0 图片加载与显示
PictureSelector 相册选图与头像上传
skin-support 4.0.5 动态主题换肤

2.2 整体架构

项目采用经典的分层架构:

复制代码
UI 层:Activity / Fragment / Adapter
    │
业务层:Okhttp(网络) + DBHelper 系列 DAO(本地数据库)
    │
数据层:entity(本地实体) + bean(网络响应模型) + utils(工具类)
  • BaseActivity 统一处理换肤逻辑;
  • MyApplication 全局初始化 skin-support;
  • DBHelper 作为 SQLite 单例入口,管理 7 张表的 DAO;
  • Okhttp 封装 POST/PUT/GET/DELETE/文件上传等通用网络方法。

三、核心功能模块与运行截图

3.1 登录与注册

启动页 OpenActivity 申请必要权限后进入登录界面。登录区分普通用户与管理员角色,支持离线登录模式。

注册页支持填写用户名、性别、手机号、地址、密码,并可通过相册选择头像。

3.2 宠物健康档案

健康模块是项目的特色功能。HealthFragment 以列表形式展示当前用户的宠物档案,包含年龄、体重、疫苗、上次洗澡时间等关键信息。

点击列表项进入编辑页,支持修改宠物类型、疫苗、洗澡日期、头像等。

3.3 在线商城

UserShopFragment 顶部使用 ViewPager 实现广告轮播,下方为商品列表,支持关键字搜索。

3.4 宠物社区

社区模块展示用户发布的图文帖子,点击可进入详情页查看、点赞、评论。

3.5 商品详情、购物车与订单

商品详情页展示大图、价格、分类,支持加入购物车与立即购买。

购物车支持勾选商品并实时计算总价。

下单后可在"我的订单"中查看历史记录。

3.6 个人中心与主题换肤

"我的"页面提供个人信息、我的订单、个性主题、退出登录、关于软件等入口。

关于页面展示应用 Logo 与简介。

社区详情页支持评论与点赞。


四、数据库设计

应用使用 SQLite 本地数据库 petHealthApp.db,数据库创建时预置了管理员、普通用户、宠物档案、社区帖子及评论等数据,方便离线演示。

主要表结构如下:

表名 核心字段 说明
user id, name, password, address, sex, phone, photo, user_type 用户表,区分管理员/普通用户
Stuff id, name, title, kind, price, path, inventory 商品表
cart id, username 购物车关联表
Record username, id, name, price, address 订单记录表
petBasic id, user_id, name, type, birth_date, weight, vaccine_name, shower_date, path 宠物健康档案
community id, user_id, title, photo, content, date, likes 社区帖子
communityComments id, user_id, community_id, name, comment_text, comment_date 社区评论

建表逻辑集中在 DBHelper.onCreate() 中:

java 复制代码
// 用户表
String sql = "create table user(id integer primary key autoincrement,name text not null," +
        "password text not null,address text not null,sex text not null," +
        "phone text not null,photo text not null,user_type text not null)";
sqLiteDatabase.execSQL(sql);

// 宠物健康档案表
sql = "CREATE TABLE petBasic (id integer primary key autoincrement,user_id integer," +
        "name text not null, type text not null, birth_date text not null," +
        "weight text not null,vaccine_name text not null,shower_date text not null,path text not null)";
sqLiteDatabase.execSQL(sql);

// 社区帖子表
sql = "CREATE TABLE community (id integer primary key autoincrement,user_id integer," +
        "title text not null, photo text not null, content text not null," +
        "date text not null, likes INTEGER)";
sqLiteDatabase.execSQL(sql);

五、关键代码解析

5.1 换肤框架初始化

MyApplication 中初始化 skin-support,关闭状态栏和窗口背景换肤,避免与系统主题冲突。

java 复制代码
public class MyApplication extends Application {
    public static MyApplication Instance;
    public static boolean offlineLogin = true;

    @Override
    public void onCreate() {
        super.onCreate();
        Instance = this;

        SkinCompatManager.withoutActivity(this)
                .addInflater(new SkinAppCompatViewInflater())
                .addInflater(new SkinMaterialViewInflater())
                .setSkinStatusBarColorEnable(false)
                .setSkinWindowBackgroundEnable(false)
                .loadSkin();
    }
}

5.2 BaseActivity 动态主题切换

所有 Activity 继承 BaseActivity,通过 SPUtils.getTheme() 读取用户保存的主题类型,在 onCreate() 中应用对应颜色。

java 复制代码
public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setTheme();
    }

    protected void setTheme() {
        int type = SPUtils.getTheme(this);
        if (type == 0) {
            setTheme(R.style.AppTheme);
            SkinCompatUserThemeManager.get().addColorState(R.color.colorPrimary,
                    new ColorState.ColorBuilder()
                            .setColorDefault("#09C6A0")
                            .setColorActivated("#09C6A0").build());
        } else if (type == 1) {
            setTheme(R.style.AppTheme1);
            SkinCompatUserThemeManager.get().addColorState(R.color.colorPrimary,
                    new ColorState.ColorBuilder()
                            .setColorDefault("#000000")
                            .setColorActivated("#000000").build());
        } else if (type == 2) {
            setTheme(R.style.AppTheme3);
            SkinCompatUserThemeManager.get().addColorState(R.color.colorPrimary,
                    new ColorState.ColorBuilder()
                            .setColorDefault("#6633FF")
                            .setColorActivated("#6633FF").build());
        }
    }

    @NonNull
    @Override
    public AppCompatDelegate getDelegate() {
        return SkinAppCompatDelegateImpl.get(this, this);
    }
}

5.3 底部导航与 Fragment 切换

MainActivity 使用 BottomNavigationView 实现底部 Tab,点击不同菜单项切换对应 Fragment。

java 复制代码
navigation.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.home:
                getSupportFragmentManager().beginTransaction()
                        .replace(R.id.main, new HealthFragment()).commit();
                return true;
            case R.id.cart:
                if (DBUser.isAdmin()) {
                    getSupportFragmentManager().beginTransaction()
                            .replace(R.id.main, FragmentMainAdmin.newInstance()).commit();
                } else {
                    getSupportFragmentManager().beginTransaction()
                            .replace(R.id.main, UserShopFragment.newInstance()).commit();
                }
                return true;
            case R.id.community:
                getSupportFragmentManager().beginTransaction()
                        .replace(R.id.main, new CommunityFragment()).commit();
                return true;
            case R.id.me:
                getSupportFragmentManager().beginTransaction()
                        .replace(R.id.main, BlankFragment_me.newInstance()).commit();
                return true;
        }
        return false;
    }
});
navigation.setSelectedItemId(R.id.home);

5.4 宠物健康数据读写

HealthFragmentDBHelper 读取当前用户的宠物档案,使用 PetHealthAdapter 展示;长按可删除,点击可编辑。

java 复制代码
@Override
public void onResume() {
    super.onResume();
    petBasic.clear();
    petBasic.addAll(DBHelper.GetInstance(getContext()).getDBPetHealth().getAll());
    set();
}

void set() {
    PetHealthAdapter adapter = new PetHealthAdapter(getContext(),
            R.layout.layout_pet_health_list, petBasic);
    listview.setAdapter(adapter);

    listview.setOnItemClickListener((adapterView, view, i, l) -> {
        Intent intent = new Intent();
        intent.putExtra("entry", petBasic.get(i));
        intent.setClass(getContext(), AddPetHealthActivity.class);
        startActivity(intent);
    });

    listview.setOnItemLongClickListener((parent, view, position, id) -> {
        new AlertDialog.Builder(getActivity())
                .setMessage("确认要删除该数据吗")
                .setPositiveButton("确定", (dialog1, which) -> {
                    DBHelper.GetInstance(getContext()).getDBPetHealth()
                            .delete(petBasic.get(position).getId());
                    petBasic.clear();
                    petBasic.addAll(DBHelper.GetInstance(getContext()).getDBPetHealth().getAll());
                    set();
                })
                .setNeutralButton("取消", (dialog, which) -> dialog.dismiss())
                .show();
        return true;
    });
}

5.5 社区详情点赞与评论

CommunityDetailActivity 使用 RecyclerView 展示评论列表,支持发表评论和点赞切换。

java 复制代码
like.setOnClickListener(v -> {
    if (bean.getLike() == 0) {
        like.setImageResource(R.drawable.like);
        bean.setLike(1);
    } else {
        like.setImageResource(R.drawable.unlike);
        bean.setLike(0);
    }
    DBHelper.GetInstance(this).getDBCommunity().change(bean);
});

findViewById(R.id.bt_comment).setOnClickListener(v -> {
    if (comment.getText().toString().isEmpty()) {
        Toast.makeText(this, "请输入评论", Toast.LENGTH_LONG).show();
        return;
    }
    saveData();
});

private void saveData() {
    CommunityComment comment1 = new CommunityComment();
    comment1.setComment_text(comment.getText().toString());
    comment1.setCommunity_id(bean.getId());
    comment1.setName(DBUser.user.getName());
    comment1.setComment_date(DBHelper.getDate());
    DBHelper.GetInstance(this).getCommunityComment().add(comment1);
    update();
}

5.6 OkHttp 网络请求封装

Okhttp 工具类采用单例模式,封装了 GET/POST/PUT/DELETE 及文件上传方法,并通过 initList() 从后端拉取商品列表。

java 复制代码
public static Okhttp getInstance() {
    if (netokhttp == null) {
        netokhttp = new Okhttp();
    }
    return netokhttp;
}

public void postJson(String url, String json, Callback callback) {
    MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    RequestBody body = RequestBody.create(JSON, json);
    Request request = new Request.Builder()
            .url(http + url)
            .post(body)
            .build();
    Call call = getInstance().initOkHttpClient().newCall(request);
    call.enqueue(callback);
}

public void uploadFile(String path, Callback callback) {
    File file = new File(path);
    RequestBody body = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("file", file.getName(),
                    MultipartBody.create(MediaType.parse("multipart/form-data"), file))
            .build();
    Request request = new Request.Builder()
            .url(http + fileUpload)
            .post(body)
            .build();
    getInstance().initOkHttpClient().newCall(request).enqueue(callback);
}

网络层基础地址配置为:

java 复制代码
private String http = "http://192.168.3.6:8080/order/";

实际运行前请根据本地后端服务地址修改该 IP。


六、项目运行说明

6.1 环境要求

  • Android Studio 4.0+
  • JDK 1.8
  • Android SDK 30
  • Gradle 插件版本与项目配置匹配

6.2 运行步骤

  1. 使用 Android Studio 打开 petHealthApp 模块;
  2. 同步 Gradle,下载依赖;
  3. 如需联网功能,修改 Okhttp.java 中的后端地址;
  4. 连接真机或启动模拟器(建议 API 21 以上);
  5. 点击 Run 安装运行。

6.3 预置账号

账号 密码 角色
admin 123 管理员
小李 123 普通用户
小王 123 普通用户

七、总结与展望

本项目从实际需求出发,将宠物健康管理、电商购物、社区互动整合到一款 Android 应用中,覆盖了登录注册、数据库设计、网络请求、图片选择、主题换肤等常见开发场景,适合作为 Android 课程设计或毕业设计参考。

后续可拓展方向:

  • 接入后端真实接口,实现用户注册、商品上下架、订单支付等完整链路;
  • 使用 Room 替换原生 SQLite,提升数据库可维护性;
  • 引入 MVVM + Jetpack 组件(ViewModel、LiveData)优化架构;
  • 增加疫苗提醒、洗澡提醒等定时通知功能;
  • 使用 Jetpack Compose 重构 UI,提升开发效率。

如果文章对你有帮助,欢迎点赞、收藏、关注!有问题可以在评论区留言交流。

相关推荐
、如果1 小时前
ima Skills 自进化实战:三层结构、反馈与版本验证
android·人工智能·ai编程
阳明山水1 小时前
销量预测2025—2026:技术演进、实证发现与系统架构的系统性综述
人工智能·深度学习·算法·机器学习·架构
谢文峰1 小时前
当每个 Agent Skill 都在构建记忆层,记忆就已经成为 Runtime 基础设施
架构
hunterandroid1 小时前
自定义 View 绘制与触摸冲突:从滑动卡顿到事件分发闭环
android·前端
预知同行1 小时前
AI Native 应用缓存架构实战:语义缓存、Prompt 缓存与响应缓存的三层设计
后端·架构
weixin_403810131 小时前
安卓自动化脚本 HID 硬件选购指南:蓝牙HID与OTG HID区别、成本与搭建教程
android·运维·自动化·hid·安卓自动化脚本·安卓中控
预知同行2 小时前
AI 应用的可观测性设计:从 OpenTelemetry GenAI 语义约定到生产级落地架构
架构
TunerT_TQ2 小时前
证据优先:从“我认为”到“我证明”——大模型工程的第一性原理(第1期)
安全·架构
qq_316837752 小时前
uniapp 安卓/H5 使用 contentEditable 属性 富文本编辑器
android·javascript·uni-app