关联章节
- HarmonyOS第一课05:案例
- HarmonyOS第一课05:应用架构设计基础——MVVM模式
- HarmonyOS第一课05:应用架构设计基础——三层架构
- HarmonyOS第一课06:ArkWeb页面适配
- HarmonyOS第一课06:通过结构化数据构建页面
说明:本章代码基于HarmonyOS第一课06:通过结构化数据构建页面代码
页面效果
文件变动
代码
default
pages/Index.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
import { QuickStartPage } from '@ohos/quickstart' import { CourseLearning } from '@ohos/learning' import { KnowledgeMap } from '@ohos/map' @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.6)') .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'))) } .scrollable(false) .vertical(false) .divider({ strokeWidth: 0.5, color: '#0D182431' }) .backgroundColor('#F1F3F5') .padding({ top: 36, bottom: 28 }) } } |
map
pages/KnowledgeMap.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
import { NavBarItemType, NavBarItem } from '../view/NavBarItem' import { KnowledgeMapContent, Section } from '../view/KnowledgeMapContent' 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: '更多' } ] @State sections: Section[] = [] @Provide('knowledgeMapPageStack') knowledgeMapPageStack: NavPathStack = new NavPathStack() @State currentNavBarIndex: number = -1 @Builder PageMap(name: string) { if (name === 'KnowledgeMapContent') { KnowledgeMapContent({ section: this.sections[this.currentNavBarIndex] }) } } 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)}`) } } aboutToAppear(): void { this.getSections() } build() { 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(14) .fontColor('rgba(0, 0, 0, 0.6)') .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") } .backgroundColor('#F1F3F5') .align(Alignment.TopStart) .constraintSize({ minHeight: '100%' }) .edgeEffect(EdgeEffect.Spring) } .hideTitleBar(true) .navDestination(this.PageMap) .mode(NavigationMode.Stack) } } |
view/KnowledgeMapContent.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
interface KnowledgeBaseItem { type: string, title: string } interface Material { subtitle: string, knowledgeBase: KnowledgeBaseItem[] } export interface Section { title: string, brief: string, materials: Material[] } 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 @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) } .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) => { ListItem() { this.KnowledgeBlockLine(item) } }, (item: KnowledgeBaseItem, index: number) => item.title) } .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() { 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.6)') .textAlign(TextAlign.JUSTIFY) .fontWeight(400) .margin({ top: 12 }) ForEach(this.section.materials, (material: Material) => { this.KnowledgeBlock(material) }, (material: Material, index: number) => material.subtitle) } .alignItems(HorizontalAlign.Start) .padding({ top: 12, left: 16, bottom: 12, right: 16 }) .backgroundColor('#F1F3F5') .width('100%') } .align(Alignment.TopStart) .constraintSize({ minHeight: '100%' }) .edgeEffect(EdgeEffect.Spring) .scrollable(ScrollDirection.Vertical) .scrollBar(BarState.Auto) .backgroundColor('#F1F3F5') } .hideTitleBar(true) } } |
view/NavBarItem.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
export interface NavBarItemType { order: string title: string } @Component export struct NavBarItem { @Prop navBarItem: NavBarItemType @Consume('knowledgeMapPageStack') knowledgeMapPageStack: NavPathStack @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') .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 this.knowledgeMapPageStack.replacePath({ name: 'KnowledgeMapContent' }) }) } } |
quickstart
pages/QuickStartPage.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
import { TutorialView } from '../view/TutorialView' import { Banner } from '../view/Banner' import { EnablementView } from '../view/EnablementView' import { ArticleClass } from '../model/ArticleClass' import { BannerClass } from '../model/BannerClass' import { ArticleDetailPage } from './ArticleDetailPage' import { BannerDetailPage } from './BannerDetailPage' @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) .fontFamily('HarmonyHeiTi-Bold') .fontSize(24) .fontWeight(700) .lineHeight(33) .width('100%') .textAlign(TextAlign.Start) .margin({ left: 16 }) Scroll() { Column() { Banner() EnablementView() TutorialView() } } .layoutWeight(1) .scrollBar(BarState.Off) .align(Alignment.TopStart) } .height('100%') .width('100%') .backgroundColor('#F1F3F5') } .navDestination(this.quickStartRouter) .hideTitleBar(true) .mode(NavigationMode.Stack) } } |
pages/ArticleDetailPage.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
import { ArticleClass } from '../model/ArticleClass' import { webview } from '@kit.ArkWeb' @Preview @Component export struct ArticleDetailPage { @State articleDetail: ArticleClass | null = null @Consume('articlePathStack') articlePathStack: NavPathStack 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(() => { this.articlePathStack.pop() }) Row() { Text(this.articleDetail?.title) .fontFamily('HarmonyHeiTi-Bold') .fontSize(20) .textAlign(TextAlign.Center) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .fontWeight(700) .margin({ left: 8 }) } } .width('80%') } .justifyContent(FlexAlign.SpaceBetween) .width('100%') .height(56) WebComponent({ articleDetail: this.articleDetail }) } .padding({ left: 16, right: 16 }) .width('100%') .width('100%') .height('100%') .justifyContent(FlexAlign.SpaceBetween) } .hideTitleBar(true) } } @Component struct WebComponent { @Prop articleDetail: ArticleClass | null private webviewController: webview.WebviewController = new webview.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) } } } |
pages/BannerDetailPage.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import { BannerClass } from '../model/BannerClass' import { webview } from '@kit.ArkWeb' @Component export struct BannerDetailPage { @State bannerClass: BannerClass | null = null controller: webview.WebviewController = new webview.WebviewController() @Consume('articlePathStack') articlePathStack: NavPathStack aboutToAppear(): void { this.bannerClass = this.articlePathStack.getParamByName('bannerDetailPage')[0] as BannerClass } build() { NavDestination() { Column() { Web({ src: this.bannerClass?.url, controller: this.controller }) .darkMode(WebDarkMode.Auto) .domStorageAccess(true) .zoomAccess(true) .fileAccess(true) .mixedMode(MixedMode.All) .javaScriptAccess(true) .width('100%') .layoutWeight(1) } } .width('100%') .height('100%') } } |
view/Banner.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
import { BannerClass } from '../model/BannerClass' import { bufferToString } from '../util/BufferUtil' @Preview @Component export struct Banner { @State bannerList: BannerClass[] = [] @Consume('articlePathStack') articlePathStack: NavPathStack getBannerDataFromJSON() { getContext(this).resourceManager.getRawFileContent('BannerData.json').then(value => { // 获取buffer内容 // let buffer: ArrayBufferLike = value.buffer // 转换为字符串 // let res: string = bufferToString(buffer) // 解析为数据结构 // this.bannerList = JSON.parse(res) as BannerClass[] // 简写 this.bannerList = JSON.parse(bufferToString(value.buffer)) as BannerClass[] }) } aboutToAppear(): void { this.getBannerDataFromJSON() } build() { Swiper() { ForEach(this.bannerList, (item: BannerClass, index: number) => { Image($r(item.imageSrc)) .objectFit(ImageFit.Contain) .width('100%') .padding({ top: 11, left: 16, right: 16 }) .borderRadius(16) .onClick(() => { this.articlePathStack.pushPathByName('bannerDetailPage', item) }) }, (item: BannerClass, index: number) => item.id) } .autoPlay(true) .loop(true) .indicator( new DotIndicator() .color('#1a000000') .selectedColor('#0A59F7') ) } } |
view/EnablementView.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
import { ArticleClass } from '../model/ArticleClass' import { bufferToString } from '../util/BufferUtil' @Component struct EnablementItem { @Prop enablementItem: ArticleClass build() { Column() { Image($r(this.enablementItem.imageSrc)) .width('100%') .objectFit(ImageFit.Cover) .height(96) .borderRadius({ topLeft: 16, topRight: 16 }) Text(this.enablementItem.title) .height(19) .width('100%') .fontSize(14) .textAlign(TextAlign.Start) .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) } } @Preview @Component export struct EnablementView { @State enablementList: Array<ArticleClass> = [] @Consume('articlePathStack') articlePathStack: NavPathStack aboutToAppear(): void { this.getEnablementDataFromJSON() } getEnablementDataFromJSON() { getContext(this).resourceManager.getRawFileContent('EnablementData.json').then(value => { this.enablementList = JSON.parse(bufferToString(value.buffer)) as ArticleClass[] }) } build() { Column() { Text('赋能套件') .width('100%') .fontColor('#182431') .fontSize(16) .fontWeight(500) .fontFamily('HarmonyHeiTi-medium') .textAlign(TextAlign.Start) .padding({ left: 16 }) .margin({ bottom: 8.5 }) 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 }) } .width('100%') .margin({ top: 18 }) } } |
view/TutorialView.ets
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
import { ArticleClass } from '../model/ArticleClass' import { bufferToString } from '../util/BufferUtil' @Component 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) .alignItems(HorizontalAlign.Start) .margin({ right: 12 }) Image($r(this.tutorialItem.imageSrc)) .height(64) .width(108) .objectFit(ImageFit.Cover) .borderRadius(16) } .width('100%') .height(88) .borderRadius(16) .backgroundColor(Color.White) .padding(12) .alignItems(VerticalAlign.Top) } } @Preview @Component export struct TutorialView { @State tutorialList: Array<ArticleClass> = [] @Consume('articlePathStack') articlePathStack: NavPathStack aboutToAppear(): void { this.getTutorialDataFromJSON() } getTutorialDataFromJSON() { getContext(this).resourceManager.getRawFileContent('TutorialData.json').then(value => { this.tutorialList = JSON.parse(bufferToString(value.buffer)) as ArticleClass[] }) } build() { Column() { Text('入门教程') .fontColor('#182431') .fontSize(16) .fontWeight(500) .fontFamily('HarmonyHeiTi-medium') .textAlign(TextAlign.Start) .padding({ left: 16 }) .margin({ bottom: 8.5 }) 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 }) } .width('100%') .margin({ top: 18 }) .alignItems(HorizontalAlign.Start) } } |
下载源码
官网地址
https://developer.huawei.com/consumer/cn/training/course/slightMooc/C101717497640610394