HarmonyOS智慧农业管理应用开发教程--高高种地---第2篇:应用架构设计与导航框架
│ 表现层 (Presentation) ││ 业务逻辑层 (Service) ││ 数据层 (Data) ││ 第三方服务 (External) ││ 高德地图 SDK / AI Kit │架构优势✅ 职责清晰,易于维护✅ 业务逻辑与 UI 分离✅ 便于单元测试✅ 支持功能扩展打开"color": [},},},},},},表现层(Pages/Components)负责 UI 展示和用户交互使用
第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:
/**
* 应用常量定义
*/
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:
/**
* 通用数据模型定义
*/
/**
* 应用模式枚举
*/
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:
/**
* 数据存储工具类
* 封装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 中添加存储键定义:
/**
* 存储键常量
*/
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:
/**
* 主题管理器
* 单例模式管理应用主题
*/
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:
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,配置页面路由:
{
"src": [
"pages/Index"
]
}
七、配置颜色资源
7.1 添加颜色定义
打开 entry/src/main/resources/base/element/color.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,添加初始化逻辑:
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');
}
}
初始化流程:
- 初始化存储工具(StorageUtil)
- 初始化主题管理器(ThemeManager)
- 加载主页面(Index)
九、运行与测试
9.1 编译运行
- 点击"Run"按钮或按
Shift + F10 - 等待编译完成
- 应用在模拟器中启动
9.2 预期效果
应用启动后,你应该看到:
- ✅ 底部显示 5 个 Tab 标签(首页、管理、学习、考试、服务)
- ✅ 点击不同 Tab 可以切换页面
- ✅ 当前选中的 Tab 高亮显示
- ✅ 每个页面显示对应的标题文本
9.3 测试功能
测试 TabBar 导航:
- 点击"管理"Tab,页面切换到管理页面
- 点击"学习"Tab,页面切换到学习页面
- 观察 Tab 图标和文字的高亮效果
测试存储功能:
在 Index.ets 的 aboutToAppear 中添加测试代码:
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 不显示
问题:底部导航栏不显示
解决方案:
- 检查
barPosition是否设置为BarPosition.End - 检查
Tabs组件的height是否设置为100% - 确认
TabContent和tabBar配对正确
11.2 页面切换无效
问题:点击 Tab 无法切换页面
解决方案:
- 检查
index是否使用双向绑定$$this.currentTabIndex - 确认
currentTabIndex使用@Local装饰器 - 查看控制台是否有错误日志
11.3 存储初始化失败
问题:StorageUtil 初始化失败
解决方案:
- 确认在
EntryAbility.onWindowStageCreate中初始化 - 检查 context 是否正确传递
- 查看错误日志,确认权限配置
十二、代码检查清单
完成本教程后,请确认:
- ✅ 创建了
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+
更多推荐


所有评论(0)