HarmonyOS智慧农业管理应用开发教程--高高种地---第2篇:应用架构设计与导航框架

第2篇:应用架构设计与导航框架

教程目标

通过本篇教程,你将学会:

  • 理解 HarmonyOS 应用的分层架构
  • 实现 TabBar 底部导航
  • 配置页面路由
  • 搭建主题管理系统
  • 设计常量与工具类

完成本教程后,你将拥有一个完整的应用基础框架。


一、应用架构设计

1.1 分层架构概述

我们的智慧农业应用采用经典的分层架构:

复制代码
┌─────────────────────────────────────────┐
│         表现层 (Presentation)            │
│  Pages / Components / UI Logic          │
├─────────────────────────────────────────┤
│         业务逻辑层 (Service)             │
│  PlantService / FieldService / MapService│
├─────────────────────────────────────────┤
│         数据层 (Data)                    │
│  StorageUtil / Models / Constants       │
├─────────────────────────────────────────┤
│         第三方服务 (External)            │
│  高德地图 SDK / AI Kit                  │
└─────────────────────────────────────────┘

架构优势

  • ✅ 职责清晰,易于维护
  • ✅ 业务逻辑与 UI 分离
  • ✅ 便于单元测试
  • ✅ 支持功能扩展

1.2 目录结构规划

entry/src/main/ets/ 下创建以下目录结构:

复制代码
ets/
├── components/          # 公共组件
├── constants/          # 常量定义
├── models/            # 数据模型
├── pages/             # 页面
│   ├── Home/         # 首页模块
│   ├── Management/   # 管理模块
│   ├── Learning/     # 学习模块
│   ├── Exam/         # 考试模块
│   └── Services/     # 服务模块
├── services/          # 业务服务
├── utils/            # 工具类
└── entryability/     # 应用入口

二、创建常量类

2.1 创建 AppConstants.ets

ets/constants/ 目录下创建 AppConstants.ets

typescript 复制代码
/**
 * 应用常量定义
 */
export class AppConstants {
  // 应用信息
  static readonly APP_NAME = '高高种地';
  static readonly APP_VERSION = '1.0.0';
  static readonly APP_SLOGAN = '让种植更简单,让收获更丰盛';

  // 存储相关
  static readonly PREFERENCES_NAME = 'PlantingAssistantPrefs';

  // 路由路径
  static readonly ROUTE_WELCOME = 'pages/WelcomePage';
  static readonly ROUTE_INDEX = 'pages/Index';

  // 时间相关
  static readonly SECOND = 1000;
  static readonly MINUTE = 60 * 1000;
  static readonly HOUR = 60 * 60 * 1000;
  static readonly DAY = 24 * 60 * 60 * 1000;

  // 默认值
  static readonly DEFAULT_PAGE_SIZE = 20;
  static readonly DEFAULT_IMAGE_QUALITY = 80;
}

/**
 * 导航常量
 */
export class NavigationConstants {
  static readonly TAB_HOME = 0;
  static readonly TAB_MANAGEMENT = 1;
  static readonly TAB_LEARNING = 2;
  static readonly TAB_EXAM = 3;
  static readonly TAB_SERVICES = 4;
}

/**
 * 颜色常量
 */
export class ColorConstants {
  // 专业农业模式颜色
  static readonly PROFESSIONAL_PRIMARY = '#007DFF';
  static readonly PROFESSIONAL_SECONDARY = '#FF6B00';
  static readonly PROFESSIONAL_BG = '#F5F7FA';

  // 状态颜色
  static readonly SUCCESS = '#52C41A';
  static readonly WARNING = '#FAAD14';
  static readonly ERROR = '#F5222D';
  static readonly INFO = '#1890FF';
}

代码说明

  • 使用 static readonly 定义常量,确保不可修改
  • 分类管理常量,便于查找和维护
  • 使用语义化命名,提高代码可读性

三、创建数据模型

3.1 创建 CommonModels.ets

ets/models/ 目录下创建 CommonModels.ets

typescript 复制代码
/**
 * 通用数据模型定义
 */

/**
 * 应用模式枚举
 */
export enum AppMode {
  HOME_GARDENING = "home_gardening",              // 家庭园艺模式
  PROFESSIONAL_AGRICULTURE = "professional_agriculture"  // 专业农业模式
}

/**
 * 主题模式枚举
 */
export enum ThemeMode {
  LIGHT = "light",
  DARK = "dark",
  AUTO = "auto"
}

/**
 * 地理位置信息
 */
export interface LocationInfo {
  province: string;           // 省
  city: string;              // 市
  district: string;          // 区/县
  climateZone: string;       // 气候带
  latitude?: number;         // 纬度
  longitude?: number;        // 经度
}

/**
 * 用户个人资料
 */
export interface UserProfile {
  id: string;
  nickname: string;
  mode: AppMode;
  location: LocationInfo;
  createdAt: number;
  lastUpdatedAt: number;
  onboardingCompleted: boolean;
}

设计要点

  • 使用 enum 定义枚举类型
  • 使用 interface 定义数据结构
  • 添加详细的注释说明
  • 使用可选属性 ? 表示非必填字段

四、创建存储工具类

4.1 创建 StorageUtil.ets

ets/utils/ 目录下创建 StorageUtil.ets

typescript 复制代码
/**
 * 数据存储工具类
 * 封装Preferences API进行本地数据持久化
 */

import { preferences } from '@kit.ArkData';

const PREFERENCES_NAME = 'PlantingAssistantPrefs';

export class StorageUtil {
  private static dataPreferences: preferences.Preferences | null = null;

  /**
   * 初始化存储
   */
  static async init(context: Context): Promise<void> {
    try {
      if (!StorageUtil.dataPreferences) {
        StorageUtil.dataPreferences = await preferences.getPreferences(context, PREFERENCES_NAME);
        console.info('[StorageUtil] Storage initialized successfully');
      }
    } catch (error) {
      console.error('[StorageUtil] Failed to initialize storage:', error);
      throw Error('Failed to initialize storage');
    }
  }

  /**
   * 保存字符串
   */
  static async saveString(key: string, value: string): Promise<void> {
    try {
      if (!StorageUtil.dataPreferences) {
        throw Error('Storage not initialized');
      }
      await StorageUtil.dataPreferences.put(key, value);
      await StorageUtil.dataPreferences.flush();
      console.info(`[StorageUtil] Saved string for key: ${key}`);
    } catch (error) {
      console.error(`[StorageUtil] Failed to save string for key ${key}:`, error);
      throw Error(`Failed to save string for key ${key}`);
    }
  }

  /**
   * 获取字符串
   */
  static async getString(key: string, defaultValue: string = ''): Promise<string> {
    try {
      if (!StorageUtil.dataPreferences) {
        throw Error('Storage not initialized');
      }
      const value = await StorageUtil.dataPreferences.get(key, defaultValue);
      return value as string;
    } catch (error) {
      console.error(`[StorageUtil] Failed to get string for key ${key}:`, error);
      return defaultValue;
    }
  }
}

工具类特点

  • 单例模式,全局共享存储实例
  • 异步操作,避免阻塞主线程
  • 完善的错误处理
  • 详细的日志输出

4.2 添加存储键常量

StorageUtil.ets 中添加存储键定义:

typescript 复制代码
/**
 * 存储键常量
 */
export class StorageKeys {
  static readonly USER_MODE = 'user_mode';
  static readonly USER_NICKNAME = 'user_nickname';
  static readonly THEME_MODE = 'theme_mode';
  static readonly ONBOARDING_COMPLETED = 'onboarding_completed';
}

五、创建主题管理器

5.1 创建 ThemeManager.ets

ets/utils/ 目录下创建 ThemeManager.ets

typescript 复制代码
/**
 * 主题管理器
 * 单例模式管理应用主题
 */

import { StorageUtil, StorageKeys } from './StorageUtil';
import { ThemeMode } from '../models/CommonModels';

export class ThemeManager {
  private static instance: ThemeManager;
  private _themeMode: ThemeMode = ThemeMode.LIGHT;
  private _isDarkMode: boolean = false;

  private constructor() {}

  /**
   * 获取单例实例
   */
  static getInstance(): ThemeManager {
    if (!ThemeManager.instance) {
      ThemeManager.instance = new ThemeManager();
    }
    return ThemeManager.instance;
  }

  /**
   * 初始化主题
   */
  async init(): Promise<void> {
    try {
      // 从存储加载主题设置
      const savedMode = await StorageUtil.getString(StorageKeys.THEME_MODE, ThemeMode.LIGHT);
      this._themeMode = savedMode as ThemeMode;

      // 应用主题
      this.applyThemeMode(this._themeMode);

      console.info(`[ThemeManager] Theme initialized: ${this._themeMode}`);
    } catch (error) {
      console.error('[ThemeManager] Failed to initialize theme:', JSON.stringify(error));
      // 使用默认亮色主题
      this.applyThemeMode(ThemeMode.LIGHT);
    }
  }

  /**
   * 设置主题模式
   */
  async setThemeMode(mode: ThemeMode): Promise<void> {
    try {
      this._themeMode = mode;
      this.applyThemeMode(mode);

      // 保存到存储
      await StorageUtil.saveString(StorageKeys.THEME_MODE, mode);

      console.info(`[ThemeManager] Theme mode set to: ${mode}`);
    } catch (error) {
      console.error('[ThemeManager] Failed to set theme mode:', JSON.stringify(error));
      throw Error('Failed to set theme mode');
    }
  }

  /**
   * 应用主题模式
   */
  private applyThemeMode(mode: ThemeMode): void {
    try {
      let isDark = false;

      switch (mode) {
        case ThemeMode.LIGHT:
          isDark = false;
          break;
        case ThemeMode.DARK:
          isDark = true;
          break;
        case ThemeMode.AUTO:
          // 跟随系统主题
          isDark = this.getSystemIsDarkMode();
          break;
      }

      this._isDarkMode = isDark;

      // 更新AppStorage中的主题状态
      AppStorage.setOrCreate('isDarkMode', isDark);
      AppStorage.setOrCreate('themeMode', mode);

      console.info(`[ThemeManager] Applied theme: dark=${isDark}`);
    } catch (error) {
      console.error('[ThemeManager] Failed to apply theme mode:', JSON.stringify(error));
    }
  }

  /**
   * 获取系统是否为深色模式
   */
  private getSystemIsDarkMode(): boolean {
    // 默认返回浅色模式
    return false;
  }

  /**
   * 获取当前主题模式
   */
  getThemeMode(): ThemeMode {
    return this._themeMode;
  }

  /**
   * 是否为深色模式
   */
  isDarkMode(): boolean {
    return this._isDarkMode;
  }

  /**
   * 切换主题(亮色/深色)
   */
  async toggleTheme(): Promise<void> {
    const newMode = this._isDarkMode ? ThemeMode.LIGHT : ThemeMode.DARK;
    await this.setThemeMode(newMode);
  }
}

设计模式

  • 单例模式:确保全局只有一个主题管理器实例
  • 使用 AppStorage 实现主题状态的全局共享
  • 支持亮色、深色、自动三种模式

六、实现 TabBar 导航

6.1 创建主导航页面

ets/pages/ 目录下创建 Index.ets

typescript 复制代码
import { AppMode } from '../models/CommonModels';
import { StorageUtil } from '../utils/StorageUtil';

@Entry
@ComponentV2
struct Index {
  @Local currentTabIndex: number = 0;
  @Local userMode: AppMode = AppMode.PROFESSIONAL_AGRICULTURE;

  async aboutToAppear(): Promise<void> {
    console.info('[Index] aboutToAppear called');
    // 加载用户模式
    const mode = await StorageUtil.getString('user_mode', AppMode.PROFESSIONAL_AGRICULTURE);
    this.userMode = mode as AppMode;
    console.info('[Index] userMode loaded:', this.userMode);
  }

  build() {
    Tabs({ index: $$this.currentTabIndex }) {
      // 首页
      TabContent() {
        this.buildHomePage()
      }
      .tabBar(this.buildTabBar('🏡', '首页', 0))

      // 管理
      TabContent() {
        this.buildManagementPage()
      }
      .tabBar(this.buildTabBar('📋', '管理', 1))

      // 学习
      TabContent() {
        this.buildLearningPage()
      }
      .tabBar(this.buildTabBar('📚', '学习', 2))

      // 考试
      TabContent() {
        this.buildExamPage()
      }
      .tabBar(this.buildTabBar('📝', '考试', 3))

      // 服务
      TabContent() {
        this.buildServicesPage()
      }
      .tabBar(this.buildTabBar('⚙️', '服务', 4))
    }
    .barPosition(BarPosition.End)
    .barMode(BarMode.Fixed)
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.background'))
  }

  @Builder
  buildTabBar(icon: string, title: string, index: number) {
    Column({ space: 4 }) {
      Text(icon)
        .fontSize(this.currentTabIndex === index ? 24 : 20)
      Text(title)
        .fontSize(this.currentTabIndex === index ? 12 : 11)
        .fontColor(this.currentTabIndex === index ?
          $r('app.color.primary_professional') : $r('app.color.text_secondary'))
        .fontWeight(this.currentTabIndex === index ? FontWeight.Medium : FontWeight.Regular)
    }
    .width('100%')
    .height(56)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildHomePage() {
    Column() {
      Text('首页')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildManagementPage() {
    Column() {
      Text('管理')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildLearningPage() {
    Column() {
      Text('学习')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildExamPage() {
    Column() {
      Text('考试')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildServicesPage() {
    Column() {
      Text('服务')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

代码要点

  • 使用 @ComponentV2@Local 实现状态管理
  • 使用 Tabs 组件实现底部导航
  • 使用 @Builder 装饰器构建可复用的 UI 组件
  • 使用 $$ 双向绑定 TabIndex

6.2 配置页面路由

打开 entry/src/main/resources/base/profile/main_pages.json,配置页面路由:

json 复制代码
{
  "src": [
    "pages/Index"
  ]
}

七、配置颜色资源

7.1 添加颜色定义

打开 entry/src/main/resources/base/element/color.json,添加颜色资源:

json 复制代码
{
  "color": [
    {
      "name": "background",
      "value": "#F5F7FA"
    },
    {
      "name": "card_background",
      "value": "#FFFFFF"
    },
    {
      "name": "text_primary",
      "value": "#1F2329"
    },
    {
      "name": "text_secondary",
      "value": "#646A73"
    },
    {
      "name": "primary_professional",
      "value": "#007DFF"
    },
    {
      "name": "primary_home_gardening",
      "value": "#52C41A"
    },
    {
      "name": "shadow_light",
      "value": "#1A000000"
    }
  ]
}

八、初始化应用入口

8.1 修改 EntryAbility.ets

打开 entry/src/main/ets/entryability/EntryAbility.ets,添加初始化逻辑:

typescript 复制代码
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { StorageUtil } from '../utils/StorageUtil';
import { ThemeManager } from '../utils/ThemeManager';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onCreate');
  }

  onDestroy(): void {
    hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onDestroy');
  }

  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
    hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onWindowStageCreate');

    // 初始化存储
    try {
      await StorageUtil.init(this.context);
      hilog.info(0x0000, 'PlantingAssistant', 'StorageUtil initialized');
    } catch (error) {
      hilog.error(0x0000, 'PlantingAssistant', 'Failed to initialize StorageUtil: %{public}s', JSON.stringify(error));
    }

    // 初始化主题管理器
    try {
      await ThemeManager.getInstance().init();
      hilog.info(0x0000, 'PlantingAssistant', 'ThemeManager initialized');
    } catch (error) {
      hilog.error(0x0000, 'PlantingAssistant', 'Failed to initialize ThemeManager: %{public}s', JSON.stringify(error));
    }

    // 加载主页面
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'PlantingAssistant', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(0x0000, 'PlantingAssistant', 'Succeeded in loading the content');
    });
  }

  onWindowStageDestroy(): void {
    hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onForeground');
  }

  onBackground(): void {
    hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onBackground');
  }
}

初始化流程

  1. 初始化存储工具(StorageUtil)
  2. 初始化主题管理器(ThemeManager)
  3. 加载主页面(Index)

九、运行与测试

9.1 编译运行

  1. 点击"Run"按钮或按 Shift + F10
  2. 等待编译完成
  3. 应用在模拟器中启动

9.2 预期效果

应用启动后,你应该看到:

  • ✅ 底部显示 5 个 Tab 标签(首页、管理、学习、考试、服务)
  • ✅ 点击不同 Tab 可以切换页面
  • ✅ 当前选中的 Tab 高亮显示
  • ✅ 每个页面显示对应的标题文本

9.3 测试功能

测试 TabBar 导航

  1. 点击"管理"Tab,页面切换到管理页面
  2. 点击"学习"Tab,页面切换到学习页面
  3. 观察 Tab 图标和文字的高亮效果

测试存储功能

在 Index.ets 的 aboutToAppear 中添加测试代码:

typescript 复制代码
async aboutToAppear(): Promise<void> {
  // 测试存储功能
  await StorageUtil.saveString('test_key', 'test_value');
  const value = await StorageUtil.getString('test_key');
  console.info('[Index] Test storage value:', value);

  // 加载用户模式
  const mode = await StorageUtil.getString('user_mode', AppMode.PROFESSIONAL_AGRICULTURE);
  this.userMode = mode as AppMode;
}

查看日志输出,确认存储功能正常。


十、架构设计总结

10.1 分层架构优势

通过本篇教程,我们建立了清晰的分层架构:

表现层(Pages/Components)

  • 负责 UI 展示和用户交互
  • 使用 ArkUI 声明式语法
  • 通过 @State/@Local 管理状态

业务逻辑层(Services)

  • 封装业务逻辑
  • 提供数据操作接口
  • 使用单例模式管理服务

数据层(Utils/Models)

  • 数据持久化(StorageUtil)
  • 数据模型定义(Models)
  • 工具类封装(Utils)

10.2 设计模式应用

单例模式

  • StorageUtil:全局共享存储实例
  • ThemeManager:全局主题管理

建造者模式

  • @Builder 装饰器构建可复用 UI

观察者模式

  • @State/@Local 实现状态响应式更新

十一、常见问题

11.1 TabBar 不显示

问题:底部导航栏不显示

解决方案

  1. 检查 barPosition 是否设置为 BarPosition.End
  2. 检查 Tabs 组件的 height 是否设置为 100%
  3. 确认 TabContenttabBar 配对正确

11.2 页面切换无效

问题:点击 Tab 无法切换页面

解决方案

  1. 检查 index 是否使用双向绑定 $$this.currentTabIndex
  2. 确认 currentTabIndex 使用 @Local 装饰器
  3. 查看控制台是否有错误日志

11.3 存储初始化失败

问题:StorageUtil 初始化失败

解决方案

  1. 确认在 EntryAbility.onWindowStageCreate 中初始化
  2. 检查 context 是否正确传递
  3. 查看错误日志,确认权限配置

十二、代码检查清单

完成本教程后,请确认:

  • ✅ 创建了 constants/AppConstants.ets
  • ✅ 创建了 models/CommonModels.ets
  • ✅ 创建了 utils/StorageUtil.ets
  • ✅ 创建了 utils/ThemeManager.ets
  • ✅ 创建了 pages/Index.ets
  • ✅ 配置了颜色资源
  • ✅ 修改了 EntryAbility.ets
  • ✅ TabBar 导航正常工作
  • ✅ 存储功能正常工作

十三、下一步

恭喜你完成了第二篇教程!你已经建立了应用的基础架构和导航框架。

在下一篇教程中,我们将学习:

  • 公共组件设计与实现
  • 深色模式支持
  • 响应式布局适配
  • 动画效果实现

准备工作

  • 熟悉 ArkUI 组件库
  • 了解 @Builder 和 @Styles 装饰器
  • 阅读 HarmonyOS 布局文档

参考资料


教程版本 :v1.0
更新日期 :2026-01
适用版本:DevEco Studio 5.0+, HarmonyOS API 17+

相关推荐
HarmonyOS_SDK2 小时前
【FAQ】HarmonyOS SDK 闭源开放能力 — Media Kit
harmonyos
不会写代码0002 小时前
Flutter 框架跨平台鸿蒙开发 - 实时彩票开奖查询应用开发教程
flutter·华为·harmonyos
夜雨声烦丿2 小时前
Flutter 框架跨平台鸿蒙开发 - 在线翻译拍照版应用开发教程
flutter·华为·harmonyos
小白阿龙3 小时前
鸿蒙+flutter 跨平台开发——物品过期追踪器开发实战
jvm·flutter·harmonyos·鸿蒙
猛扇赵四那边好嘴.3 小时前
Flutter 框架跨平台鸿蒙开发 - 亲子成长记录应用开发教程
flutter·华为·harmonyos
哈哈你是真的厉害3 小时前
小白基础入门 React Native 鸿蒙跨平台开发:模拟一个URL解析工具
react native·react.js·harmonyos
djarmy3 小时前
【开源鸿蒙跨平台 flutter选型】为开源鸿蒙跨平台工程集成网络请求能力,实现数据清单列表的完整构建与开源鸿蒙设备运行验证
flutter·华为·harmonyos
小白阿龙3 小时前
鸿蒙+flutter 跨平台开发——支持自定义的打印纸生成器实战
flutter·华为·harmonyos·鸿蒙
小风呼呼吹儿3 小时前
Flutter 框架跨平台鸿蒙开发 - 运动健身打卡:打造你的专属健身助手
flutter·华为·harmonyos