当前位置: 首页 > article >正文

HarmonyOS三层架构实战

目录:

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

1、三层架构项目结构

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

1.0、三层架构简介

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

1.4、MVVM模式

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

2、products主页面

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

在这里插入图片描述

Index.ets

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为BarPosition.End时,页签栏会位于应用界面的下方进行显示
    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 {
  //相信大家还记得,完成了Navigation导航首页的内容开发,接下来就要切到Navigation非首页里进行操作,首先,我们在Banner文件中使用@Consume拿到路由栈,这样才能使用相关的功能
  @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) {
    //让我们为每个Banner的图片添加一个点击事件,当点击Banner时,使用路由栈提供的pushPathByName方法,并提供url作为路由参数
    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;
  }
} 

ArticleDetailPage.ets

import { webview } from '@kit.ArkWeb';
import { ArticleClass } from '../model/ArticleClass'

@Component
export struct ArticleDetailPage {
  @State webviewController: webview.WebviewController = new webview.WebviewController;
  @Consume('articlePathStack') articlePathStack: NavPathStack;
  //在其中进行数据结构的定义,该页面在之后会接收到一个ArticleClass类型的数据,由于之后会使用路由栈进行传参所以此处使用@State进行定义
  @State articleDetail: ArticleClass | null = null;

  aboutToAppear(): void {
    this.articleDetail = this.articlePathStack.getParamByName('articleDetail')[0] as ArticleClass;
  }

  build() {
    NavDestination() {
      Column() {
        Row() {
          Row() {
            Image($r('app.media.ic_back'))
              .width(40)
              .height(40)
              .onClick(() => {
              //使用路由栈的pop方法来实现页面返回
                this.articlePathStack.pop()
              })
            Row() {
              Text(this.articleDetail?.title)
                .fontFamily('HarmonyHeiTi-Bold')
                .fontSize(20)
                .textAlign(TextAlign.Start)
                .textOverflow({ overflow: TextOverflow.Ellipsis })
                .maxLines(1)
                .fontWeight(700)
                .margin({ left: 8 })
            }
          }
          .width('80%')
        }
        .justifyContent(FlexAlign.SpaceBetween)
        .width('100%')
        .height(56)

        WebComponent({ articleDetail: this.articleDetail, webviewController: this.webviewController })
      }
      .padding({ left: 16, right: 16 })
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.SpaceBetween)
    }
    .hideTitleBar(true)
  }
}

@Component
struct WebComponent {
  @Prop articleDetail: ArticleClass | null;
  @Prop webviewController: WebviewController;

  build() {
    Column() {
      Web({ src: this.articleDetail?.webUrl, controller: this.webviewController })
        .darkMode(WebDarkMode.Auto)
        .domStorageAccess(true)
        .zoomAccess(true)
        .fileAccess(true)
        .mixedMode(MixedMode.All)
        .cacheMode(CacheMode.None)
        .javaScriptAccess(true)
        .width('100%')
        .layoutWeight(1)
    }
  }
}

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加载刚刚放入rawfile目录下的course_learning资源
      Web({ src: $rawfile('course_learning/index.html'), controller: this.webviewController })
        //设置domStorageAccess属性,开启文档对象模型存储接口权限
        .domStorageAccess(true)
    }
  }
}

5、知识地图模块

在这里插入图片描述

KnowledgeMap.ets

import { KnowledgeMapContent, Section } from '../view/KnowledgeMapContent';
import { NavBarItem, NavBarItemType } from '../view/NavBarItem';
import { BusinessError } from '@kit.BasicServicesKit';
import { util } from '@kit.ArkTS';

@Component
export struct KnowledgeMap {
  @State navBarList: NavBarItemType[] = [
    { order: '01', title: '准备与学习' },
    { order: '02', title: '构建应用' },
    { order: '03', title: '应用测试' },
    { order: '04', title: '上架' },
    { order: '05', title: '运营增长' },
    { order: '06', title: '商业变现' },
    { order: '07', title: '更多' }
  ];
  //在KnowledgeMap页面中定义路由栈。我们通过创建NavPathStack实例并定义为@Provide类型的状态变量来进行路由栈的定义。Navigation有属于自己的路由栈,可以用于路由的前进、回退、路由历史记录的保存、路由参数的保存与获取。所以首先我们需要创建一个路由栈。

  //注意此处采用@Provide的方式进行定义,由于之后会将该路由栈的数据传递给跳转后的后代组件,为了方便后代组件的获取,所以此处采用了@Provide的方式进行定义。
  @Provide('knowledgeMapPageStack') knowledgeMapPageStack: NavPathStack = new NavPathStack();
  @State currentNavBarIndex: number = -1;
  @State sections: Section[] = [];

  private getSections() {
    try {
      getContext(this).resourceManager.getRawFileContent("MapData.json", (error: BusinessError, value: Uint8Array) => {
        const textDecoder = util.TextDecoder.create("utf-8");
        const res = textDecoder.decodeWithStream(value, { stream: false });
        this.sections = JSON.parse(res);
      });
    } catch (error) {
      console.error(`callback getRawFileContent failed, error is ${JSON.stringify(error)}`)
    }
  }
//在KnowledgeMap组件内定义getSections函数,使用util类来实现将json文件转化为前端数据。具体实现参考右侧代码。最终将getSections函数放入组件的aboutToAppear生命周期内,创建自定义组件的新实例后可执行getSections函数
  aboutToAppear(): void {
    this.getSections();
  }

  @Builder
  PageMap(name: string) {
    if (name === 'KnowledgeMapContent') {
      KnowledgeMapContent({ section: this.sections[this.currentNavBarIndex] });
    }
  }

  build() {
    //绑定路由栈到Navigation组件
    Navigation(this.knowledgeMapPageStack) {
      Scroll() {
        Column() {
          Text('知识地图')
            .fontFamily('HarmonyHeiTi-Bold')
            .fontSize(24)
            .fontColor(Color.Black)
            .textAlign(TextAlign.Start)
            .lineHeight(33)
            .fontWeight(700)
            .width('100%')
          Image($r("app.media.knowledge_map_banner"))
            .width('100%')
            .borderRadius(16)
            .margin({ top: 19, bottom: 8 })
          Text('通过循序渐进的学习路径,无经验和有经验的开发者都可以轻松掌握ArkTS语言声明式开发范式,体验更简洁、更友好的HarmonyOS应用开发旅程。')
            .fontFamily('HarmonyHeiTi')
            .fontSize('14vp')
            .fontColor('rgba(0,0,0,0.60)')
            .fontWeight(400)
            .textAlign(TextAlign.Start)

          List({ space: 12 }) {
            ForEach(this.navBarList, (item: NavBarItemType, index: number) => {
              ListItem() {
                NavBarItem({ navBarItem: item, currentNavBarIndex: this.currentNavBarIndex })
              }
              .width('100%')
            }, (item: NavBarItemType): string => item.title)
          }
          .width('100%')
          .margin({ top: 24 })
        }
        .padding({
          top: 12,
          right: 16,
          bottom: 12,
          left: 16
        })
      }
      .backgroundColor('#F1F3F5')
      .align(Alignment.TopStart)
      //此处添加constraintSize并设置minHeight为100%,可以解决当内容条数不足时,Scroll组件滚动时会出现空白区域的错误效果
      .constraintSize({ minHeight: '100%' })
      .scrollable(ScrollDirection.Vertical)
      .scrollBar(BarState.Auto)
      .scrollBarColor(Color.Gray)
      //edgeEffect用于设置边缘滑动效果,设置为EdgeEffect.Spring表示设置为弹性物理动效。该效果滑动到边缘后可以根据初始速度或通过触摸事件继续滑动一段距离,松手后回弹
      .edgeEffect(EdgeEffect.Spring)
    }
    //控制导航栏显示模式。可以通过Navigation的mode属性控制导航栏的显示模式,该属性支持以下的取值:
	//将mode属性为NavigationMode.Auto,为自适应模式,即当设备宽度大于520vp时,Navigation组件采用分栏模式,反之采用单页面模式。
	//将mode属性设置为NavigationMode.Stack,Navigation组件即可设置为单页面显示模式。
	//将mode属性设置为NavigationMode.Split,Navigation组件即可设置为分栏显示模式。
	//结合之前的效果图,在手机设备上为单页面显示模式,所以我们设置mode属性并传入NavigationMode.Stack参数。
    .mode(NavigationMode.Stack)
    .hideTitleBar(true)
    //关联NavDestination组件与Navigation组件。我们需要关联NavDestination组件与Navigation组件,才能在Navigation组件中使用页面名称的方式来进行组件路由。具体的关联方式为使用Navigation的navDestination属性,该属性支持传入一个自定义构建函数,我们可以使用之前定义的PageMap来作为参数传入。
    .navDestination(this.PageMap)
    .navBarWidth(288)
  }
}

NavBarItem.ets

export interface NavBarItemType {
  order: string,
  title: string
}

@Component
export struct NavBarItem {
  @Consume('knowledgeMapPageStack') knowledgeMapPageStack: NavPathStack;
  //由于此处只需要单向的从外部获得参数并进行渲染,后续将改为@Prop;@State是双向的
  @Prop navBarItem: NavBarItemType;
  @Link currentNavBarIndex: number;

  build() {
    Row() {
      Text(this.navBarItem.order)
        .margin({ right: 6 })
        .fontFamily('HarmonyHeiTi-Bold')
        .fontSize(21)
        .fontColor('#182431')
        .textAlign(TextAlign.Start)
        .lineHeight(22)
        .fontWeight(700)
      Text(this.navBarItem.title)
        .fontFamily('HarmonyHeiTi-Medium')
        .fontSize(16)
        .fontColor('#182431')
        .textAlign(TextAlign.Start)
        .lineHeight(22)
        .fontWeight(500)
      Blank()
      Image($r('app.media.ic_arrow'))
        .width(12)
        .height(24)
    }
    .width('100%')
    .height(48)
    .borderRadius(16)
    .alignItems(VerticalAlign.Center)
    .padding({ left: 12, right: 12 })
    .backgroundColor(
      this.currentNavBarIndex === Number(this.navBarItem.order) - 1 ?
        '#1A0A59F7' :
      Color.Transparent
    )
    .onClick(() => {
      const index = Number(this.navBarItem.order) - 1;
      this.currentNavBarIndex = index;
      //实现路由跳转。通过自定义导航条的点击事件实现路由Navigation组件的跳转。首先通过@Consume获取到祖代组件传递过来的路由栈数据,然后使用该路由栈实现路由的跳转,Navigation支持的跳转方式主要有两种,分别为push的方式与replace的方式。
	//pushPath()/pushPathByName():根据传入的参数将参数对应的NavDestination页面信息入栈。
	//replacePath()/replacePathByName():将当前页面栈栈顶退出,再根据传入参数将对应的NavDestination页面信息入栈。
	//现在给NavBar加上点击事件用于路由跳转,根据需求,此处采用replacePath的方式,该方法需要传入一个NavPathInfo类型的对象,可以包含路由的页面名称,携带的参数等信息,此处只需要用到路由名称。
      this.knowledgeMapPageStack.replacePath({ name: 'KnowledgeMapContent' });
    })
  }
}

在这里插入图片描述

KnowledgeMapContent.ets

interface KnowledgeBaseItem {
  type: string,
  title: string
}

interface Material {
  subtitle: string,
  knowledgeBase: KnowledgeBaseItem[]
}

export interface Section {
  title: string,
  brief: string,
  materials: Material[]
}
//定义类型与图标的映射。此时图标已经放入media目录下,我们需要考虑如何设计来映射type与图标。可以使用Record类型来进行映射,也可以使用Map进行映射,此处采用Record类型
const TypeMapIcon: Record<string, string> = {
  '指南': 'app.media.ic_guide',
  '准备': 'app.media.ic_prepare',
  '学习与获取证书': 'app.media.ic_medals',
  '视频教程': 'app.media.ic_video',
}

@Component
export struct KnowledgeMapContent {
  @Prop section: Section;
  scroller: Scroller = new Scroller();

  @Builder
  KnowledgeBlockLine(knowledgeBaseItem: KnowledgeBaseItem) {
    Row() {
      Image($r(TypeMapIcon[knowledgeBaseItem.type]))
        .width(20)
        .height(20)

      Column() {
        Text(knowledgeBaseItem.title)
          .fontFamily('HarmonyHeiTi-Medium')
          .fontSize(16)
          .fontWeight(500)
        Text(knowledgeBaseItem.type)
          .fontFamily('HarmonyHeiTi')
          .fontSize(14)
          .fontWeight(400)
      }
      //Column组件容器的alignItems属性默认为HorizontalAlign.Center。这里我们要实现起始端对齐的效果,因此这里的属性参数设定为HorizontalAlign.Start
      .alignItems(HorizontalAlign.Start)
      .margin({ left: 18 })

      Blank()

      Image($r('app.media.ic_arrow'))
        .width(12)
        .height(24)
    }
    .width('100%')
    .height(64)
    .alignItems(VerticalAlign.Center)
  }

  @Builder
  KnowledgeBlock(material: Material) {
    Column() {
      Text(material.subtitle)
        .fontFamily('HarmonyHeiTi-Medium')
        .fontSize(14)
        .fontWeight(500)
        .margin({ bottom: 8 })

      List({ space: 12 }) {
        ForEach(material.knowledgeBase, (item: KnowledgeBaseItem, index: number) => {
          this.KnowledgeBlockLine(item)
        }, (item: KnowledgeBaseItem, index: number) => item.title + index)
      }
      .backgroundColor(Color.White)
      .borderRadius(16)
      .padding({ left: 12, right: 12 })
      .divider({
        strokeWidth: 0.5,
        startMargin: 38,
        endMargin: 0,
        color: '#F2F2F2'
      })
    }
    .width('100%')
    .margin({ top: 28 })
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    NavDestination() {
      Scroll(this.scroller) {
        Column() {
          Text(this.section?.title)
            .fontFamily('HarmonyHeiTi-Bold')
            .fontSize(20)
            .fontWeight(700)
            .fontColor(Color.Black)

          Text(this.section?.brief)
            .fontFamily('HarmonyHeiTi')
            .fontSize(12)
            .fontColor('rgba(0,0,0,0.60)')
            .textAlign(TextAlign.JUSTIFY)
            .fontWeight(400)
            .margin({ top: 12 })

          ForEach(this.section?.materials, (material: Material) => {
            this.KnowledgeBlock(material)
          }, (material: Material, index: number) => material.subtitle + index)
        }
        .padding({
          left: 24,
          top: 12,
          right: 24,
          bottom: 12
        })
        .alignItems(HorizontalAlign.Start)
      }
      .align(Alignment.TopStart)
      .constraintSize({ minHeight: '100%' })
      .edgeEffect(EdgeEffect.Spring)
      .scrollable(ScrollDirection.Vertical)
      .scrollBar(BarState.Auto)
      .backgroundColor('#F1F3F5')
    }
    //在组件路由导航中,内容区都需要使用NavDestination组件进行包裹,否则会导致点击后内容区为白屏,所以需要在features/map/src/main/ets/view/KnowledgeMapContent.ets文件中build函数内最外层添加一层NavDestination组件,同样的,在数据驱动UI更新小节中我们已经自定义了标题栏,可以关闭NavDestination默认的标题栏(hideTitleBar)。
    .hideTitleBar(true)
  }
}

http://www.kler.cn/a/591766.html

相关文章:

  • 算法刷题记录——LeetCode篇(6) [第501~600题](持续更新)
  • 前端安全之DOMPurify基础使用
  • pytorch小记(十三):pytorch中`nn.ModuleList` 详解
  • 【华为OD-E卷 - 单词接龙 100分(python、java、c++、js、c)】
  • linux系统 Ubuntu22.04安装Nvidia驱动,解决4060系列显卡重启黑屏方法
  • 【QA】工厂模式在Qt有哪些应用?
  • 基于传感器数据的城市空气质量预测与污染源分类
  • 用hexo初始化博客执行hexo init时碰到的问题
  • Linux的Shell编程
  • 学习网络层
  • 手搓智能音箱——语音识别及调用大模型回应
  • 如何用AI轻松制作完美PPT,节省时间又提升效率
  • Python基础语法全解析:从入门到实践
  • TikTok账号养成计划:从0到1打造高权重店铺
  • Centos7使用docker搭建redis集群
  • hibernate 自动生成数据库表和java类 字段顺序不一致 这导致添加数据库数据时 异常
  • 谢希仁第八版计算机网络期末复习(自检提纲,后附大题答案)
  • 使用 OpenSSL 构建安全的网络应用
  • C语言论递归函数及其本质
  • Bug 算法路径规划:原理、推导与实现