HarmonyOS三层架构实战

news2025/3/18 13:05:08

目录:

    • 1、三层架构项目结构
      • 1.0、三层架构简介
      • 1.1、 common层(主要放一些公共的资源等)
      • 1.2、 features层(主要模块定义的组件以及图片等静态资源)
      • 1.3、 products层(主要放主页面层和一些主要的资源,内部快速入门,课程学习,知识地图等都是模块放在features层)
      • 1.4、MVVM模式
    • 2、products主页面
    • 3、快速入门模块
      • QuickStartPage.ets
      • Banner.ets
      • bufferToString.ets
      • BannerClass.ets
      • EnablementView.ets
      • TutorialView.ets
      • ArticleClass.ets
    • 4、课程学习模块
      • CourseLearning.ets
    • 5、知识地图模块

1、三层架构项目结构

在这里插入图片描述
这里新建三个文件夹不是模块,来构建鸿蒙项目的三层架构。

1.0、三层架构简介

在这里插入图片描述

1.1、 common层(主要放一些公共的资源等)

在这里插入图片描述

1.2、 features层(主要模块定义的组件以及图片等静态资源)

在这里插入图片描述

1.3、 products层(主要放主页面层和一些主要的资源,内部快速入门,课程学习,知识地图等都是模块放在features层)

在这里插入图片描述

1.4、MVVM模式

在这里插入图片描述
在这里插入图片描述

2、products主页面

这里放置的都是一些跟主页面相关的以及入口文件等。

在这里插入图片描述

import { CourseLearning } from '@ohos/learning';
import { KnowledgeMap } from '@ohos/map';
import { QuickStartPage } from '@ohos/quickstart';

@Entry
@Component
struct Index {
  @State currentIndex: number = 0;
  private tabsController: TabsController = new TabsController();

  @Builder
  tabBarBuilder(title: string, targetIndex: number, selectedIcon: Resource, unselectIcon: Resource) {
    Column() {
      Image(this.currentIndex === targetIndex ? selectedIcon : unselectIcon)
        .width(24)
        .height(24)
      Text(title)
        .fontFamily('HarmonyHeiTi-Medium')
        .fontSize(10)
        .fontColor(this.currentIndex === targetIndex ? '#0A59F7' : 'rgba(0,0,0,0.60)')
        .textAlign(TextAlign.Center)
        .lineHeight(14)
        .fontWeight(500)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .onClick(() => {
      this.currentIndex = targetIndex;
      this.tabsController.changeIndex(targetIndex);
    })
  }

  build() {
    Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
      TabContent() {
        QuickStartPage()
      }
      .tabBar(this.tabBarBuilder('快速入门', 0, $r('app.media.ic_01_on'), $r('app.media.ic_01_off')))

      TabContent() {
        CourseLearning()
      }
      .tabBar(this.tabBarBuilder('课程学习', 1, $r('app.media.ic_02_on'), $r('app.media.ic_02_off')))

      TabContent() {
        KnowledgeMap()
      }
      .tabBar(this.tabBarBuilder('知识地图', 2, $r('app.media.ic_03_on'), $r('app.media.ic_03_off')))
    }
    .vertical(false)
    .divider({
      strokeWidth: 0.5,
      color: '#0D182431'
    })
    .scrollable(false)
    .backgroundColor('#F1F3F5')
    .padding({ top: 36, bottom: 28 })
  }
}

以前导出模块都是在各模块index.ets文件下导出,现在是products依赖feature模块,在oh-package.json5中如下配置即可引入相关依赖。

在这里插入图片描述
在module.json5中进行权限配置(这里其他模块需要的权限都在products中的这个配置文件中配置即可)。

在这里插入图片描述

3、快速入门模块

在这里插入图片描述

QuickStartPage.ets

import { TutorialView } from '../view/TutorialView';
import { ArticleClass } from '../model/ArticleClass'
import { ArticleDetailPage } from './ArticleDetailPage';
import { Banner } from '../view/Banner';
import { EnablementView } from '../view/EnablementView';
import { BannerDetailPage } from './BannerDetailPage';
import { BannerClass } from '../model/BannerClass';

@Component
export struct QuickStartPage {
  @State message: string = '快速入门';
  @Provide('articlePathStack') articlePathStack: NavPathStack = new NavPathStack();

  @Builder
  quickStartRouter(name: string, param?: ArticleClass | BannerClass) {
    if (name === 'articleDetail') {
      ArticleDetailPage()
    } else if (name === 'bannerDetailPage') {
      BannerDetailPage()
    }
  }

  build() {
    Navigation(this.articlePathStack) {
      Column() {
        Text(this.message)
          .fontSize(24)
          .fontWeight(700)
          .width('100%')
          .textAlign(TextAlign.Start)
          .padding({ left: 16 })
          .fontFamily('HarmonyHeiTi-Bold')
          .lineHeight(33)
        //此处采用Scroll作为外层容器,是由于其内部内容很有可能会超过屏幕高度,为保证内容显示,可以采用Scroll组件来进行滚动显示。scrollBar设置为BarState.Off,表示关闭滚动时的滚动条显示
        Scroll() {
          Column() {
            //轮播图广告组件
            Banner()
            //赋能组件
            EnablementView()
            //入门教程
            TutorialView()
          }
        }
        .layoutWeight(1)
        .scrollBar(BarState.Off)
        .align(Alignment.TopStart)
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#F1F3F5')
    }
    .navDestination(this.quickStartRouter)
    .hideTitleBar(true)
    .mode(NavigationMode.Stack)
  }
}

Banner.ets

import { BannerClass } from '../model/BannerClass';
import { bufferToString } from '../util/BufferUtil';

@Component
export struct Banner {
  @Consume('articlePathStack') articlePathStack: NavPathStack;
  @State bannerList: BannerClass[] = [];

  aboutToAppear(): void {
    //在组件初始化时就加载数据
    this.getBannerDataFromJSON()
  }
  //在Banner中,定义一个方法getBannerDataFromJson,并通过ResourceManager获取当前工程目录下rawfile中的json文件内容。
  //转换内容需要两个步骤:
  //1、将获取的buffer内容转换为字符串
  //2、将字符串转换为页面数据结构

//因为预览器并不支持获取rawfile目录下的文件,所以无法成功获取到保存在rawfile目录下的json文件里的内容。请用真机/模拟器测试,预览器仅支持简单页面的预览
  getBannerDataFromJSON() {
    getContext(this).resourceManager.getRawFileContent('BannerData.json').then(value => {
      this.bannerList = JSON.parse(bufferToString(value)) as BannerClass[];
    })
  }

  clickToDetailPage(item: BannerClass) {
    this.articlePathStack.pushPathByName('bannerDetailPage', item);
  }

  build() {
    //Swiper组件作为容器可以使轮播图具有轮播的效果
    Swiper() {
      ForEach(this.bannerList, (item: BannerClass) => {
        //$r("字符串类型的")
        Image($r(item.imageSrc))
          .objectFit(ImageFit.Contain) //保持宽高比进行缩小或者放大
          .width('100%')
          .borderRadius(16)
          .padding({ top: 11, left: 16, right: 16 })
          .onClick(() => {
            this.clickToDetailPage(item)
          })

      }, (item: BannerClass) => item.id)
    }
    //autoPlay控制是否自动轮播子组件,loop属性控制是否循环播放,indicator属性自定义导航点的位置和样式
    .autoPlay(true)
    .loop(true)
    .indicator(
      new DotIndicator()
        .color('#1a000000')
        .selectedColor('#0A59F7'))
  }
}

bufferToString.ets

import { util } from '@kit.ArkTS';
//由于ResourceManager获取到的是Uint8Array类型的内容,所以需要将对应的内容转换为字符串,并将字符串解析为对应的数据结构。考虑到其他的文件也会使用这个公共方法,可以新建一个util文件夹,并创建一个BufferUtil文件,实现这个字符串转换方法
export function bufferToString(buffer: Uint8Array): string {
  let textDecoder = util.TextDecoder.create('utf-8', {
    ignoreBOM: true
  });
  let resultPut = textDecoder.decodeToString(buffer);
  return resultPut;
}

BannerClass.ets

export class BannerClass {
  id: string = '';
  imageSrc: string = '';
  url: string = ''

  constructor(id: string, imageSrc: string, url: string) {
    this.id = id
    this.imageSrc = imageSrc;
    this.url = url;
  }
}

EnablementView.ets

import { ArticleClass } from '../model/ArticleClass';
import { bufferToString } from '../util/BufferUtil';

@Component
export struct EnablementView {
  @State enablementList: ArticleClass[] = [];
  @Consume('articlePathStack') articlePathStack: NavPathStack;

  aboutToAppear(): void {


----------


    this.getEnablementDataFromJSON()
  }

  getEnablementDataFromJSON() {
    getContext(this).resourceManager.getRawFileContent('EnablementData.json').then(value => {
      this.enablementList = JSON.parse(bufferToString(value)) as ArticleClass[];
    })
  }

  build() {
    Column() {
      Text('赋能套件')
        .fontColor('#182431')
        .fontSize(16)
        .fontWeight(500)
        .fontFamily('HarmonyHeiTi-medium')
        .textAlign(TextAlign.Start)
        .padding({ left: 16, right: 16 })
        .width('100%')
      Grid() {
        ForEach(this.enablementList, (item: ArticleClass) => {
          GridItem() {
            EnablementItem({ enablementItem: item })
              .onClick(() => {
                this.articlePathStack.pushPathByName('articleDetail', item)
              })
          }
        }, (item: ArticleClass) => item.id)
      }
      .rowsTemplate('1fr')
      .columnsGap(8)
      .scrollBar(BarState.Off)
      .height(169)
      .padding({ top: 2, left: 16, right: 16 })
    }
    .margin({ top: 18 })
  }
}

@Component
export struct EnablementItem {
  @Prop enablementItem: ArticleClass;

  build() {
    Column() {
      Image($r(this.enablementItem.imageSrc))
        .width('100%')
        //设置填充效果为cover模式,即保持宽高比进行缩小或者放大,使得图片两边都大于或等于显示边界
        .objectFit(ImageFit.Cover)
        .height(96)
        .borderRadius({
          topLeft: 16,
          topRight: 16
        })
      Text(this.enablementItem.title)
        .height(19)
        .width('100%')
        .fontSize(14)
        .textAlign(TextAlign.Start)
        //textOverFlow属性设置文本超长时的显示方式,在这里我们设置它的值为Ellipsis,表示超长时使用省略号替代
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .maxLines(1)
        .fontWeight(400)
        .padding({ left: 12, right: 12 })
        .margin({ top: 8 })
      Text(this.enablementItem.brief)
        .height(32)
        .width('100%')
        .fontSize(12)
        .textAlign(TextAlign.Start)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .maxLines(2)
        .fontWeight(400)
        .fontColor('rgba(0, 0, 0, 0.6)')
        .padding({ left: 12, right: 12 })
        .margin({ top: 2 })
    }
    .width(160)
    .height(169)
    .borderRadius(16)
    .backgroundColor(Color.White)
  }
}

TutorialView.ets

import { bufferToString } from '../util/BufferUtil';
import { ArticleClass } from '../model/ArticleClass';

@Component
export struct TutorialView {
  @State tutorialList: ArticleClass[] = [];
  @Consume('articlePathStack') articlePathStack: NavPathStack;

  aboutToAppear(): void {
    this.getTutorialDataFromJSON()
  }

  getTutorialDataFromJSON() {
    getContext(this).resourceManager.getRawFileContent('TutorialData.json').then(value => {
      this.tutorialList = JSON.parse(bufferToString(value)) as ArticleClass[];
    })
  }

  build() {
    Column() {
      Text('入门教程')
        .fontColor('#182431')
        .fontSize(16)
        .fontWeight(500)
        .fontFamily('HarmonyHeiTi-medium')
        .textAlign(TextAlign.Start)
        .padding({ left: 16, right: 16 })
        .width('100%')

      List({ space: 12 }) {
        ForEach(this.tutorialList, (item: ArticleClass) => {
          ListItem() {
            TutorialItem({ tutorialItem: item })
              .onClick(() => {
                this.articlePathStack.pushPathByName('articleDetail', item)
              })
          }
        }, (item: ArticleClass) => item.id)
      }
      .scrollBar(BarState.Off)
      .padding({ left: 16, right: 16 })
    }
    .margin({ top: 18 })
    .alignItems(HorizontalAlign.Start)
  }
}

@Component
export struct TutorialItem {
  @Prop tutorialItem: ArticleClass;

  build() {
    Row() {
      Column() {
        Text(this.tutorialItem.title)
          .height(19)
          .width('100%')
          .fontSize(14)
          .textAlign(TextAlign.Start)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .maxLines(1)
          .fontWeight(400)
          .margin({ top: 4 })

        Text(this.tutorialItem.brief)
          .height(32)
          .width('100%')
          .fontSize(12)
          .textAlign(TextAlign.Start)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .maxLines(2)
          .fontWeight(400)
          .fontColor('rgba(0, 0, 0, 0.6)')
          .margin({ top: 5 })
      }
      .height('100%')
      //设置layoutWeight属性,取值为1,表示它们在任意尺寸的设备下自适应占满剩余空间
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
      .margin({ right: 12 })

      Image($r(this.tutorialItem.imageSrc))
        .objectFit(ImageFit.Cover)
        .height(64)
        .width(108)
        .borderRadius(16)
    }
    .width('100%')
    .height(88)
    .borderRadius(16)
    .backgroundColor(Color.White)
    .padding(12)
    .alignItems(VerticalAlign.Top)
  }
}

ArticleClass.ets

export class ArticleClass {
  id: string = '';
  imageSrc: string = '';
  title: string = '';
  brief: string = '';
  webUrl: string = '';

  constructor(id: string, imageSrc: string, title: string, brief: string, webUrl: string) {
    this.id = id;
    this.imageSrc = imageSrc;
    this.title = title;
    this.brief = brief;
    this.webUrl = webUrl;
  }
} 

4、课程学习模块

CourseLearning.ets

import { webview } from '@kit.ArkWeb';

@Component
export struct CourseLearning {
  //创建webviewController,开发者后续可以通过该Controller控制Web组件加载的界面
  private webviewController: webview.WebviewController = new webview.WebviewController();

  build() {
    Column() {
      Web({ src: $rawfile('course_learning/index.html'), controller: this.webviewController })
        .domStorageAccess(true)
    }
  }
}

5、知识地图模块

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317238.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机四级 - 数据库原理 - 第4章 「关系数据库标准语言SQL」

4.1 SQL概述 4.1.1 结构化查询语言SQL SQL(Structured Query Language)称为结构化查询语言,它是由1974年由Boyce和Chamberi提出的,1975年至1979年IBM公司的San Jose Research Laboratory研制了关系数据库管理系统的原型系统System R,并实现了这种语198…

基于PMU的14节点、30节点电力系统状态估计MATLAB程序

“电气仔推送”获得资料(专享优惠) 程序简介: 程序采用三种方法对14节点和30节点电力系统状态进行评估: ①PMU同步向量测量单元结合加权最小二乘法(WLS)分析电力系统的电压幅值和相角状态; …

Deepseek API+Python测试用例一键生成与导出-V1.0.2【实现需求文档图片识别与用例生成自动化】

在测试工作中,需求文档中的图片(如界面设计图、流程图)往往是测试用例生成的重要参考。然而,手动提取图片并识别内容不仅耗时,还容易出错。本文将通过一个自研小工具,结合 PaddleOCR 和大模型,自…

整形在内存中的存储(例题逐个解析)

目录 一.相关知识点 1.截断: 2.整形提升: 3.如何 截断,整型提升? (1)负数 (2)正数 (3)无符号整型,高位补0 注意:提升后得到的…

蓝牙系统的核心组成解析

一、硬件层:看得见的物理载体 1. 射频模块(Radio Frequency Module) 专业描述:工作在2.4GHz ISM频段,支持GFSK/π/4 DQPSK/8DPSK调制方式 功能类比:相当于人的"嘴巴"和"耳朵" 发射端…

uniapp笔记-底部和首部标签页菜单生成

逻辑 这些都是需要配置pages.json文件。 其中底部需要手动配置tarBar,如: "tabBar": {"list":[{"pagePath": "pages/index/index","text": "首页"},{"pagePath": "pages/…

SpringBoot 和vue前后端配合开发网页拼图10关游戏源码技术分享

今天分享一个 前后端结合 的网页游戏 开发项目源码技术。 这也是我第一次写游戏类的程序,虽然不是特别复杂的游戏,但是是第一次写,肯定要记录一下了,哈哈。 游戏的内容 就是 我们显示中玩的那个 拼图碎片的 游戏,类似下…

OpenCV计算摄影学(21)非真实感渲染之边缘保留滤波器edgePreservingFilter()

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 滤波是图像和视频处理中的基础操作。边缘保留平滑滤波器被广泛应用于多种不同场景[98]。 cv::edgePreservingFilter 是一种边缘保留滤波器&#…

JVM并发编程AQSsync锁ReentrantLock线程池ThreadLocal

并发编程2 synchronized锁实现**AQS****ReentrantLock实现****JUC 常用类**池的概念 ThreadLocalThreadLocal原理内存泄露强引用:软引用弱引用虚引用ThreadLocal内存泄露 synchronized锁实现 synchronized是一个关键字,实现同步,还需要我们提供一个同步锁对象,记录锁状态,记录…

什么是数学建模?数学建模是将实际问题转化为数学问题

数学建模是将实际问题转化为数学问题,并通过数学工具进行分析、求解和验证的过程。 一、数学建模的基本流程 问题分析 • 明确目标:确定需要解决的核心问题。 • 简化现实:识别关键变量、忽略次要因素。 • 定义输入和输出:明确模…

唤起“队列”的回忆

又来博客记录自己的学习心得了,嘿嘿嘿(^~^) 目录 队列的概念和结构: 队列的创建和初始化: 队列入栈: 队列出栈: 队列的销毁: 取队头和队尾数据: 结语: 队列的概念…

Linux(8.4)NFS

文章目录 一、概念二、详解NFS1)软件名2)服务名3)配置文件4)端口号5)相关命令 三、部署NFS一、NFS服务端1)**配置源(本地或者网络源)**2)2、安装NFS**3)启动服…

【位运算】速算密钥:位运算探秘

文章目录 前言例题一、判定字符是否唯一二、丢失的数字三、两整数之和四、只出现⼀次的数字 II五、消失的两个数字 结语 前言 什么是位运算算法呢? 位运算算法是以位运算为核心操作,设计用来高效解决特定问题的一系列计算步骤集合。它巧妙利用位运算直接…

STM32G070CBT6读写FLASH中的数据

向FLASH中写入数据函数 /*函数说明:向FLASH中写数据形参:addr-要写入数据的起始地址 data-准备写入数据 len-数据大小返回值:1-成功,0-失败 */ uint8_t FlashWriteData(uint64_t addr,uint8_t data[],size_t len) {uint32_t Fir…

AD绘图基本操作

一、基本操作 注意:快捷键都要在英文模式下才能生效 1、移动 按住鼠标右键移动 2、切换桌面栅格距离 G 3、英寸和毫米 尺寸切换 Q 4、元件在3D模式下的移动 3D视角鼠标左键只起到选择元器件并移动之的功能, 单纯鼠标右键只能平移桌面 shift鼠…

dfs(十二)21. 合并两个有序链表 递归解决

21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 2: 输入:l1 [], l2 [] …

51单片机指令系统入门

目录 基本概念讲解 一、机器指令​ 二、汇编指令​ (一)汇编指令的一般格式 (二)按字节数分类的指令 三、高级指令 总结​ 基本概念讲解 指令是计算机(或单片机)中 CPU 能够识别并执行的基本操作命令…

安全无事故连续天数计算,python 时间工具的高效利用

安全天数计算,数据系统时间直取,安全标准高效便捷好用。 笔记模板由python脚本于2025-03-17 23:50:52创建,本篇笔记适合对python时间工具有研究欲的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值:在于输出思考与经验&am…

如何玩DeepSeek!15分钟快速创建GIS动态数据可视化仪表盘

DeepSeek最近火遍全球,大家用的都用的不亦乐乎。国外呢?当然也是,最近一上YouTube、X等都是deepseek的推送。 今天介绍一下,我在YouTube上看到的GIS行业与DeepSeek结合的一个案例: 快速轻松构建交互式地图仪表盘&…

课上测试:MIRACL共享库使用测试

MIRACL(MultiprecisionIntegerandRationalArithmeticC/cLibrary)是著名的密码算法库,设法去官网下载安装MIRACL,提交安装过程截图或过程文本(3分). 去github官网下载.zip文件 使用如下命令进行解压 unzip -j -aa -L MIRACL-mast…