鸿蒙基础入门与高频知识点梳理

打印 上一主题 下一主题

主题 576|帖子 576|积分 1728

介绍鸿蒙高频知识点,持续更新中
  一、鸿蒙代码结构

  1. ├──entry/src/main/ets        // 代码区
  2. │  ├──common
  3. │  │  └──Constant.ets        // 常量类
  4. │  ├──entryability            
  5. │  │  └──EntryAbility.ts     // 程序入口类
  6. │  ├──pages
  7. │  │  ├──MainPage.ets        // 主页入口文件
  8. │  │  └──WebPage.ets         // 抽奖页入口文件
  9. │  └──viewmodel                          
  10. │     └──NavigatorModel.ets  // 导航model
  11. ├──entry/src/main/resources  
  12. │  ├──base
  13. │  │  ├──element             // 尺寸、颜色、文字等资源文件存放位置
  14. │  │  ├──media               // 媒体资源存放位置
  15. │  │  └──profile             // 页面配置文件存放位置
  16. │  ├──en_US                  // 国际化英文
  17. │  ├──rawfile                // 本地html代码存放位置
  18. │  └──zh_CN                  // 国际化中文
  19. └──HttpServerOfWeb           // 服务端代码
复制代码
二、设置文件

1、module.json5

用于设置UIAbility页面模块信息。
位置:/entry/src/main/module.json5
  1. {
  2.   "module": {
  3.     "name": "entry",//当前Module的名称
  4.     "type": "entry",//Module的类型(entry:应用的主模块, feature:应用的动态特性模块)
  5.     "description": "$string:module_desc",
  6.     "mainElement": "EntryAbility",//标识当前Module的入口UIAbility名称或者ExtensionAbility名称。
  7.     "deviceTypes": [//运行设备
  8.       "phone",
  9.       "tablet"
  10.     ],
  11.     "deliveryWithInstall": true,//标识当前Module是否在用户主动安装的时候安装,表示该Module对应的HAP是否跟随应用一起安装。
  12.     "installationFree": false,//是否支持免安装特性
  13.     "pages": "$profile:main_pages",//页面配置文件json
  14.     "abilities": [//UIAbility的配置信息
  15.       {
  16.         "name": "EntryAbility",//当前UIAbility组件的名称,该名称在整个应用要唯一
  17.         "srcEntry": "./ets/entryability/EntryAbility.ts",//入口UIAbility的路径
  18.         "description": "$string:EntryAbility_desc",
  19.         "icon": "$media:icon",//app图标
  20.         "label": "$string:EntryAbility_label",//app
  21.         "startWindowIcon": "$media:icon",//当前UIAbility组件启动页面图标(暂时没发现有啥用,与上面保持一致即可)
  22.         "startWindowBackground": "$color:start_window_background",
  23.         "exported": true,//当前UIAbility组件是否可以被其他应用调用
  24.         "skills": [//能够接收的Want的特征集
  25.           {
  26.             "entities": [
  27.               "entity.system.home"
  28.             ],
  29.             "actions": [
  30.               "action.system.home"
  31.             ]
  32.           }
  33.         ]
  34.       }
  35.     ]
  36.   }
  37. }
复制代码
2、main_pages.json

页面列表json,对应上面module.json5的pages字段。
位置:/entry/src/main/resources/base/profile/main_pages.json

  1. {
  2.   "src": [
  3.     "pages/SecondPage",
  4.     "pages/SimpleVideoPlay",
  5.     "pages/Index"
  6.   ]
  7. }
复制代码
3、build-profile.json5

定制HAP多目标构建产物。
位置:entry/build-profile.json5
  1. {
  2.   "apiType": 'stageMode',
  3.   "buildOption": {
  4.   },
  5.   "targets": [
  6.     {
  7.       "name": "default",
  8.       "runtimeOS": "HarmonyOS"
  9.     },
  10.     {
  11.       "name": "ohosTest",
  12.     }
  13.   ]
  14. }
复制代码
例如,以ArkTS Stage模型为例,界说一个免费版和付费版,示例如下。参考资料
  1. {
  2.   "apiType": 'stageMode',
  3.   "buildOption": {
  4.   },
  5.   "targets": [
  6.     {
  7.       "name": "default"  //默认target名称default,未定义deviceType,默认支持config.json或module.json5中定义的设备类型
  8.     },
  9.     {
  10.       "name": "free", //免费版target名称
  11.       "config": {
  12.         "deviceType": [  //定义free支持的设备类型为Tablet
  13.           "tablet"
  14.         ]
  15.       }
  16.     },
  17.     {
  18.       "name": "pay",//付费版target名称
  19.       "config": {
  20.         "deviceType": [  //定义pay支持的设备类型为Tablet
  21.           "tablet"
  22.         ]
  23.       }
  24.     }
  25.   ]
  26. }
复制代码
4、oh-package.json5

形貌项目基础信息
位置:entry/oh-package.json5
  1. {
  2.   "name": "entry",
  3.   "version": "1.0.0",
  4.   "description": "Please describe the basic information.",
  5.   "main": "",
  6.   "author": "",
  7.   "license": "",
  8.   "dependencies": {}
  9. }
复制代码
三、组件

1、Image



  • 网络图片
   需要在module.json5 文件中添加网络访问权限
  1. "module": {
  2.   "requestPermissions": [
  3.     {"name": "ohos.permission.INTERNET"}
  4.   ]
  5. }
复制代码
  1. Image('https://gitcode.net/liuxingyuzaixian/csdn_img/-/raw/main/pictures/2023/11/17_10_51_42_image-20230518181509168.png')
  2.   .width(78)
  3.   .height(78)
  4.   .objectFit(ImageFit.Cover)//设置缩放类型
复制代码


  • PixelMap 图片
   代码天生的色块图片,需要创建PixelMap对象
  1. @State myPixelmap?: PixelMap = null
  2. onPageShow() {
  3.   // 创建PixelMap图片
  4.   const color = new ArrayBuffer(56);
  5.   let opts = { editable: true, pixelFormat: 3, size: { height: 4, width: 6 } }
  6.   image.createPixelMap(color, opts, (err, pixelmap) => {
  7.     if (pixelmap != undefined) {
  8.       this.myPixelmap = pixelmap;
  9.     }
  10.   })
  11. }
  12. // 使用
  13. if (this.myPixelmap != null)
  14.   Image(this.myPixelmap).width(78).height(78)
复制代码


  • Resource 图片
   需要将图片添加到下面目录:/resources/base/media
  1. // 使用
  2. Image($r('app.media.icon')).width(78).height(78)
复制代码
2、Text

  1. Text($r('app.string.module_desc'))
  2.   .fontSize(50)
  3.   .fontWeight(FontWeight.Bold)
  4.   .fontColor(0xFF0000)
  5.   .maxLines(1)
  6.   .textOverflow({ overflow: TextOverflow.Ellipsis })//单行...
  7.   .decoration({ type: TextDecorationType.Underline, color: Color.Black })//文本装饰线
复制代码
1、数字默认单位: vp
2、vp :屏幕密度相关像素
3、sp:文本推荐
3、TextInput

   单行文本输入
  1. TextInput({ placeholder: "账号" })
  2.   .maxLength(11)
  3.   .type(InputType.Number)
  4.   .onChange((value: string) => {
  5.   })
复制代码
4、Button

  1. Button("登录", { type: ButtonType.Capsule })
  2.   .onClick(() => {
  3.   })
复制代码
5、Column、Row

   用法语 flutter 一样,仅仅多了space参数方便添加间距
  1. Column({ space: 10 }) {
  2.   Text("asdf")
  3.   Text("asdf")
  4. }.alignItems(HorizontalAlign.Start)
复制代码
6、List

   如果长度超过容器高度,就会滚动
  1. private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  2. List({ space: 10 }) {
  3.   ForEach(this.arr, (item: number) => {
  4.     ListItem() {
  5.       Text(`${item}`)
  6.         .width('100%')
  7.         .height(100)
  8.         .fontSize(20)
  9.         .fontColor(Color.White)
  10.         .textAlign(TextAlign.Center)
  11.         .borderRadius(10)
  12.         .backgroundColor(0x007DFF)
  13.     }
  14.   }, item => item)
  15. }
  16. .height('100%')
复制代码
7、Grid

   构建如下不可滚动网格示例
  
  1. Grid() {
  2.   ForEach(this.arr, (item: string) => {
  3.     GridItem() {
  4.       Text(item)
  5.         .fontSize(16)
  6.         .fontColor(Color.White)
  7.         .backgroundColor(0x007DFF)
  8.         .width('100%')
  9.         .height('100%')
  10.         .textAlign(TextAlign.Center)
  11.     }
  12.   }, item => item)
  13. }
  14. .columnsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局列的数量。
  15. .rowsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局行的数量。
  16. .columnsGap(10) // 设置列与列的间距。
  17. .rowsGap(10) // 设置行与行的间距。
  18. .height(300)
复制代码
  如果需要垂直方向滚动,则关闭掉rowsTemplate即可,如下:
  1. Grid() {
  2.   ForEach(this.arr, (item: string) => {
  3.     GridItem() {
  4.       Text(item)
  5.         .fontSize(16)
  6.         .fontColor(Color.White)
  7.         .backgroundColor(0x007DFF)
  8.         .width(50)
  9.         .height(50)
  10.         .textAlign(TextAlign.Center)
  11.     }
  12.   }, item => item)
  13. }
  14. .direction(Direction.Ltr)
  15. .columnsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局列的数量。
  16. // .rowsTemplate('1fr 2fr 1fr 1fr') // 设置当前网格布局行的数量。
  17. .columnsGap(10) // 设置列与列的间距。
  18. .rowsGap(10) // 设置行与行的间距。
  19. .height(300)
  20. .onScrollIndex((first: number) => {
  21.   console.info('first:' + first)
  22. })
复制代码
8、Tabs

   使用体系自带的样式:不带图片
  
  1. private controller: TabsController = new TabsController()
  2. Column() {
  3.   Tabs({ barPosition: BarPosition.End, controller: this.controller }) {
  4.     TabContent() {
  5.       Column().width('100%').height('100%').backgroundColor(Color.Green)
  6.     }
  7.     .tabBar('首页')
  8.     TabContent() {
  9.       Column().width('100%').height('100%').backgroundColor(Color.Blue)
  10.     }
  11.     .tabBar('我的')
  12.   }
  13.   .barMode(BarMode.Fixed)//页签比较多的时候,可以设置滑动页签Scrollable
  14.   .barWidth('100%') // 设置TabBar宽度
  15.   .barHeight(60) // 设置TabBar高度
  16.   .width('100%') // 设置Tabs组件宽度
  17.   .height('100%') // 设置Tabs组件高度
  18.   .backgroundColor(0xF5F5F5) // 设置Tabs组件背景颜色
  19.   .vertical(false)//注意:这个表示底部的 tab 排列方向(与页面与 tab 的排列方向刚好相反)
  20. }
  21. .width('100%')
  22. .height('100%')
复制代码
  自界说样式:带图片。tabBar组件支持@Builder装饰器修饰的函数
  
  1. struct Index {
  2.   @State currentIndex: number = 0;
  3.   private tabsController: TabsController = new TabsController();
  4.   @Builder TabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {
  5.     Column() {
  6.       Image(this.currentIndex === targetIndex ? selectedImg : normalImg)
  7.         .size({ width: 25, height: 25 })
  8.       Text(title)
  9.         .fontColor(this.currentIndex === targetIndex ? '#1698CE' : '#6B6B6B')
  10.     }
  11.     .width('100%')
  12.     .height(50)
  13.     .justifyContent(FlexAlign.Center)
  14.     .onClick(() => {
  15.       this.currentIndex = targetIndex;
  16.       this.tabsController.changeIndex(this.currentIndex);
  17.     })
  18.   }
  19.   build() {
  20.     Tabs({ barPosition: BarPosition.End, controller: this.tabsController }) {
  21.       TabContent() {
  22.         Column().width('100%').height('100%').backgroundColor('#00CB87')
  23.       }
  24.       .tabBar(this.TabBuilder('首页', 0, $r('app.media.icon'), $r('app.media.test')))
  25.       TabContent() {
  26.         Column().width('100%').height('100%').backgroundColor('#007DFF')
  27.       }
  28.       .c(this.TabBuilder('我的', 1, $r('app.media.icon'), $r('app.media.test')))
  29.     }
  30.     .barWidth('100%')
  31.     .barHeight(50)
  32.     .onChange((index: number) => {
  33.       this.currentIndex = index;
  34.     })
  35.   }
  36. }
复制代码
9、Swiper

  1. Swiper() {
  2.   Image($r('app.media.video_list0'))
  3.     .borderRadius(12).objectFit(ImageFit.Contain)
  4.   Image($r('app.media.video_list0'))
  5.     .borderRadius(12).objectFit(ImageFit.Contain)
  6.   Image($r('app.media.video_list0'))
  7.     .borderRadius(12).objectFit(ImageFit.Contain)
  8. }
  9. .autoPlay(true)
复制代码
10、Slider进度条

  1. @State slidingProgress: number = 0;
  2. // 样式 1
  3. Slider({
  4.   value: this.slidingProgress,
  5.   style: SliderStyle.InSet,
  6. })
  7.   .onChange((value: number, mode: SliderChangeMode) => {
  8.     this.slidingProgress = Math.floor(value);
  9.   })
  10. // 样式 2
  11. Slider({
  12.   value: this.slidingProgress,
  13.   style: SliderStyle.OutSet,
  14. })
  15.   .onChange((value: number, mode: SliderChangeMode) => {
  16.     this.slidingProgress = Math.floor(value);
  17.   })
复制代码
11、Video

1、加载本地
需要先在rawfile中添加videoTest.mp4文件
  1. Video({
  2.   src: $rawfile('videoTest.mp4'),
  3.   previewUri: $r('app.media.icon'),
  4. })
复制代码
效果图如下
2、加载网络视频
src换成网络视频即可,并且添加网络权限。
需要注意的是:
1、现在我使用鸿蒙模仿器对网络视频的加载体验并不好
2、网络加载器点击播放的时间需要一段下载时间,最好加上loading
  1. Video({
  2.   src: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
  3.   previewUri: $r('app.media.icon'),
  4. })
  5.   .objectFit(ImageFit.Contain)
复制代码
3、自界说Video
  1. Button("dianji").onClick(()=>{
  2.   router.pushUrl({
  3.     url: 'pages/SimpleVideoPlay',
  4.     params: { source: $rawfile('videoTest.mp4') }//添加视频资源
  5.   });
  6. })
复制代码
自界说Video页面SimpleVideoPlay.ets
   需要额外不上这些icon
ic_back.png
ic_pause.png
ic_play.png
ic_public_play.png
  1. import router from '@ohos.router';
  2. import { VideoPlayer } from './VideoPlayer';
  3. /**
  4. * 自定义Video页面
  5. */
  6. @Entry
  7. @Component
  8. struct Play {
  9.   private source: string = (router.getParams() as Record<string, Object>).source as string;
  10.   private startIconResource: Resource = $r('app.media.ic_public_play');
  11.   private backIconResource: Resource = $r('app.media.ic_back');
  12.   @Provide isPlay: boolean = false;
  13.   @Provide isOpacity: boolean = false;
  14.   controller: VideoController = new VideoController();
  15.   @Provide isLoading: boolean = false;
  16.   @Provide progressVal: number = 0;
  17.   @Provide flag: boolean = false;
  18.   aboutToAppear() {
  19.     this.source;
  20.   }
  21.   onPageHide() {
  22.     this.controller.pause();
  23.   }
  24.   build() {
  25.     Column() {
  26.       Row() {
  27.         Image(this.backIconResource)
  28.           .width(24)
  29.           .height(24)
  30.           .margin({ left: 24 })
  31.           .onClick(() => {
  32.             router.back();
  33.           })
  34.         Text('返回')
  35.           .fontColor(Color.White)
  36.           .fontSize(24)
  37.           .fontWeight(500)
  38.           .margin({ left: 12 })
  39.       }
  40.       .width('100%')
  41.       .margin({
  42.         left: 12,
  43.         top: 12
  44.       })
  45.       .justifyContent(FlexAlign.Start)
  46.       Stack() {
  47.         if (!this.isPlay && !this.isLoading) {
  48.           Image(this.startIconResource)
  49.             .width(50)
  50.             .height(50)
  51.             .zIndex(2)
  52.         }
  53.         if (this.isLoading) {
  54.           Progress({
  55.             value: 0,
  56.             total: 100,
  57.             type: ProgressType.ScaleRing
  58.           })
  59.             .color(Color.Grey)
  60.             .value(this.progressVal)
  61.             .width(80)
  62.             .style({
  63.               strokeWidth: 15,
  64.               scaleCount: 15,
  65.               scaleWidth: 5
  66.             })
  67.             .zIndex(1)
  68.         }
  69.         VideoPlayer({
  70.           source: this.source,
  71.           controller: this.controller
  72.         })
  73.           .zIndex(0)
  74.       }
  75.     }
  76.     .height('100%')
  77.     .backgroundColor(Color.Black)
  78.   }
  79. }
复制代码
滑块VideoPlaySlider.ets
  1. /**
  2. * video slider component
  3. */
  4. @Component
  5. export struct VideoSlider {
  6.   @Consume isOpacity: boolean;
  7.   private controller: VideoController = new VideoController();
  8.   @Consume currentStringTime: string;
  9.   @Consume currentTime: number;
  10.   @Consume durationTime: number;
  11.   @Consume durationStringTime: string;
  12.   @Consume isPlay: boolean;
  13.   @Consume flag: boolean;
  14.   @Consume isLoading: boolean;
  15.   @Consume progressVal: number;
  16.   build() {
  17.     Row({ space: 12 }) {
  18.       Image(this.isPlay ? $r('app.media.ic_pause') : $r('app.media.ic_play'))
  19.         .width(24)
  20.         .height(24)
  21.         .margin({ left: 12 })
  22.         .onClick(() => {
  23.           this.iconOnclick();
  24.         })
  25.       Text(this.currentStringTime)
  26.         .fontSize(16)
  27.         .fontColor(Color.White)
  28.         .margin({ left: 12 })
  29.       Slider({
  30.         value: this.currentTime,
  31.         min: 0,
  32.         max: this.durationTime,
  33.         step: 1,
  34.         style: SliderStyle.OutSet
  35.       })
  36.         .blockColor("#FFFFFF")
  37.         .width('46.7%')
  38.         .trackColor(Color.Gray)
  39.         .selectedColor("#FFFFFF")
  40.         .showSteps(true)
  41.         .showTips(true)
  42.         .trackThickness(this.isOpacity ? 2 : 4)
  43.         .onChange((value: number, mode: SliderChangeMode) => {
  44.           this.sliderOnchange(value, mode);
  45.         })
  46.       Text(this.durationStringTime)
  47.         .fontSize(16)
  48.         .margin({ right: 12 })
  49.         .fontColor(Color.White)
  50.     }
  51.     .opacity(this.isOpacity ? Number.parseFloat('0.2') : 1)
  52.     .width('100%')
  53.     .alignItems(VerticalAlign.Center)
  54.     .justifyContent(FlexAlign.Center)
  55.   }
  56.   /**
  57.    * icon onclick callback
  58.    */
  59.   iconOnclick() {
  60.     if (this.isPlay === true) {
  61.       this.controller.pause()
  62.       this.isPlay = false;
  63.       this.isOpacity = false;
  64.       return;
  65.     }
  66.     if (this.flag === true) {
  67.       this.controller.start();
  68.       this.isPlay = true;
  69.       this.isOpacity = true;
  70.     } else {
  71.       this.isLoading = true;
  72.       // The video loading is not complete. The loading action is displayed.
  73.       let intervalLoading = setInterval(() => {
  74.         if (this.progressVal >= 100) {
  75.           this.progressVal = 0;
  76.         } else {
  77.           this.progressVal += 10;
  78.         }
  79.       }, 100)
  80.       // The scheduled task determines whether the video loading is complete.
  81.       let intervalFlag = setInterval(() => {
  82.         if (this.flag === true) {
  83.           this.controller.start();
  84.           this.isPlay = true;
  85.           this.isOpacity = true;
  86.           this.isLoading = false;
  87.           clearInterval(intervalFlag);
  88.           clearInterval(intervalLoading);
  89.         }
  90.       }, 100);
  91.     }
  92.   }
  93.   /**
  94.    * video slider component onchange callback
  95.    */
  96.   sliderOnchange(value: number, mode: SliderChangeMode) {
  97.     this.currentTime = Number.parseInt(value.toString());
  98.     this.controller.setCurrentTime(Number.parseInt(value.toString()), SeekMode.Accurate);
  99.     if (mode === SliderChangeMode.Begin || mode === SliderChangeMode.Moving) {
  100.       this.isOpacity = false;
  101.     }
  102.     if (mode === SliderChangeMode.End) {
  103.       this.isOpacity = true;
  104.     }
  105.   }
  106. }
复制代码
Video组件封装VideoPlayer.ets
  1. import prompt from '@ohos.promptAction';
  2. import { VideoSlider } from './VideoPlaySlider';
  3. export function changeSliderTime(value: number): string {
  4.   let second: number = value % 60;
  5.   let min: number = Number.parseInt((value / 60).toString());
  6.   let head = min < 10 ? `${'0'}${min}` : min;
  7.   let end = second < 10 ? `${'0'}${second}` : second;
  8.   let nowTime = `${head}${':'}${end}`;
  9.   return nowTime;
  10. }
  11. /**
  12. * video controller component
  13. */
  14. @Component
  15. export struct VideoPlayer {
  16.   private source: string | Resource = '';
  17.   private controller: VideoController = new VideoController();
  18.   private previewUris: Resource = $r('app.media.icon');
  19.   @Provide currentTime: number = 0;
  20.   @Provide durationTime: number = 0;
  21.   @Provide durationStringTime: string = '00:00';
  22.   @Provide currentStringTime: string = '00:00';
  23.   @Consume isPlay: boolean;
  24.   @Consume isOpacity: boolean;
  25.   @Consume flag: boolean;
  26.   @Consume isLoading: boolean;
  27.   @Consume progressVal: number;
  28.   build() {
  29.     Column() {
  30.       Video({
  31.         src: this.source,
  32.         previewUri: this.previewUris,
  33.         controller: this.controller
  34.       })
  35.         .width('100%')
  36.         .height('88%')
  37.         .controls(false)
  38.         .autoPlay(false)
  39.         .objectFit(ImageFit.Contain)
  40.         .loop(false)
  41.         .onUpdate((event) => {
  42.           if (event) {
  43.             this.currentTime = event.time;
  44.             this.currentStringTime = changeSliderTime(this.currentTime);
  45.           }
  46.         })
  47.         .onPrepared((event) => {
  48.           this.prepared(event?.duration);
  49.         })
  50.         .onFinish(() => {
  51.           this.finish();
  52.         })
  53.         .onError(() => {
  54.           prompt.showToast({
  55.             duration: 5000,
  56.             message: '请检查网络'
  57.           });
  58.         })
  59.       VideoSlider({ controller: this.controller })
  60.     }
  61.   }
  62.   /**
  63.    * video component prepared callback
  64.    */
  65.   prepared(duration: number) {
  66.     this.durationTime = duration;
  67.     let second: number = duration % 60;
  68.     let min: number = Number.parseInt((duration / 60).toString());
  69.     let head = min < 10 ? `${'0'}${min}` : min;
  70.     let end = second < 10 ? `${'0'}${second}` : second;
  71.     this.durationStringTime = `${head}${':'}${end}`;
  72.     this.flag = true;
  73.   }
  74.   /**
  75.    * video component finish callback
  76.    */
  77.   finish() {
  78.     this.isPlay = false;
  79.     this.isOpacity = false;
  80.   }
  81. }
复制代码
12、Web

1、Web组件使用
  1. struct Index {
  2.   controller: WebController = new WebController();
  3.   build() {
  4.     Column() {
  5.       // 加载网页
  6.       Web({ src: 'https://developer.harmonyos.com/', controller: this.controller })
  7.       // 加载本地html
  8.       // Web({ src: $rawfile('index.html'), controller: this.controller })
  9.     }
  10.   }
  11. }
复制代码
2、Web与js交互
下面示例中:
1、打开App,html回调confirm方法
2、点击按钮,app调用html的test方法
   鸿蒙页面使用如下
  1. struct Index {
  2.   controller: WebController = new WebController();
  3.   build() {
  4.     Column() {
  5.       // 鸿蒙调用html的方法
  6.       Button("鸿蒙按钮").onClick(() => {
  7.         this.controller.runJavaScript({
  8.           script: 'test()',
  9.           callback: (result: string) => {
  10.             prompt.showToast({
  11.               duration: 5000,
  12.               message: result
  13.             });
  14.           } });
  15.       })
  16.       Web({ src: $rawfile('index.html'), controller: this.controller })
  17.         .javaScriptAccess(true)
  18.         // 鸿蒙对外方法
  19.         .onConfirm((event) => {
  20.           AlertDialog.show({
  21.             title: 'title',
  22.             message: event.message,
  23.             confirm: {
  24.               value: 'onAlert',
  25.               action: () => {
  26.                 event.result.handleConfirm();
  27.               }
  28.             },
  29.             cancel: () => {
  30.               event.result.handleCancel();
  31.             }
  32.           })
  33.           return true;
  34.         })
  35.         // 输出js的日志
  36.         .onConsole((event) => {
  37.           console.log('getMessage:' + event.message.getMessage());
  38.           console.log('getMessageLevel:' + event.message.getMessageLevel());
  39.           return false;
  40.         })
  41.     }
  42.   }
  43. }
复制代码
  html使用如下
  1. <!DOCTYPE html>
  2. <html>
  3. <meta charset="utf-8">
  4. <body>
  5. </body>
  6. <script type="text/javascript">
  7.   <!--js回调鸿蒙的方法-->
  8.   confirm("confirm message from html")
  9.   <!--js对外方法-->
  10.   function test() {
  11.       return "This value is from index.html"
  12.   }
  13. </script>
  14. </html>
复制代码
四、鸿蒙api

1、UIAbility启动模式

   UIAbility当前支持singleton(单实例模式)、multiton(多实例模式)和specified(指定实例模式)3种启动模式
  

  • singleton(单实例模式)
   如果应用进程中该类型的UIAbility实例已经存在,则复用体系中的UIAbility实例.
  在module.json5设置文件中的"launchType"字段设置为"singleton"即可。
  1. {
  2.   "module": {
  3.     // ...
  4.     "abilities": [
  5.       {
  6.         "launchType": "singleton",
  7.         // ...
  8.       }
  9.     ]
  10.   }
  11. }
复制代码


  • standard(标准实例模式)
   每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。
  1. "launchType": "standard",
复制代码


  • specified(指定实例模式)
针对一些特殊场景使用(例如文档应用中每次新建文档希望都能新建一个文档实例,重复打开一个已保存的文档希望打开的都是同一个文档实例)
  1. "launchType": "specified",
复制代码
2、UIAbility组件生命周期

UIAbility的生命周期包罗Create、Foreground、Background、Destroy四个状态

需要注意的是:UIAbility没有WindowStageCreate、WindowStageDestroy,这两个是WindowStage的生命周期。
UIAbility实例创建完成之后,在进入Foreground之前,体系会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中设置UI界面加载、设置WindowStage的变乱订阅。
3、toast

  1. import prompt from '@ohos.promptAction';
  2. Button("点击toast").onClick(() => {
  3.   prompt.showToast({
  4.     duration: 5000,
  5.     message: '点击toast'
  6.   });
  7. })
复制代码
4、Preferences存储

   注意:初始化需要await,并且需要context参数,发起在EntryAbility的onCreate方法中
  1. await sharePreferenceUtil.init(this.context);
复制代码
  1. import dataPreferences from '@ohos.data.preferences';
  2. const KEY_APP_FONT_SIZE = 'appFontSize';
  3. /**
  4. * SP工具类
  5. */
  6. export class SharePreferenceUtil {
  7.   preferences: dataPreferences.Preferences;
  8.   // 初始化(注意:初始化是异步方法,需要await)
  9.   async init(context: Context) {
  10.     this.preferences = await dataPreferences.getPreferences(context, 'myPreferences');
  11.   }
  12.   // 存储
  13.   saveDefaultFontSize(fontSize: number) {
  14.     this.preferences.has(KEY_APP_FONT_SIZE).then(async (isExist: boolean) => {
  15.       if (!isExist) {
  16.         await this.preferences.put(KEY_APP_FONT_SIZE, fontSize);
  17.         this.preferences.flush();
  18.       }
  19.     }).catch((err: Error) => {
  20.     });
  21.   }
  22.   // 更新
  23.   async saveChangeFontSize(fontSize: number) {
  24.     await this.preferences.put(KEY_APP_FONT_SIZE, fontSize);
  25.     this.preferences.flush();
  26.   }
  27.   // 获取
  28.   async getChangeFontSize() {
  29.     let fontSize: number = 0;
  30.     fontSize = await this.preferences.get(KEY_APP_FONT_SIZE, fontSize) as number;
  31.     return fontSize;
  32.   }
  33.   // 删除
  34.   async deleteChangeFontSize() {
  35.     let deleteValue = this.preferences.delete(KEY_APP_FONT_SIZE);
  36.     deleteValue.then(() => {
  37.     }).catch((err: Error) => {
  38.     });
  39.   }
  40. }
  41. const sharePreferenceUtil = new SharePreferenceUtil();
  42. export default sharePreferenceUtil;
复制代码


五、状态管理与数据同步

1、组件状态管理装饰器和@Builder装饰器:

组件状态管理装饰器用来管理组件中的状态,它们分别是:@State、@Prop、@Link。


  • @State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法举行UI革新。
  • @Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量举行初始化,答应组件内部修改@Prop变量,但更改不会通知给父组件,即@Prop属于单向数据绑定。
  • @Link装饰的变量可以和父组件的@State变量建立双向数据绑定,需要注意的是:@Link变量不能在组件内部举行初始化。
  • @Builder装饰的方法用于界说组件的声明式UI形貌,在一个自界说组件内快速天生多个结构内容。
组件内的状态管理:@State
从父组件单向同步状态:@Prop
与父组件双向同步状态:@Link
监听状态变革:@Watch
跨组件层级双向同步状态:@Provide和@Consume
1、父页面同步数据给子页面:@Prop
2、子页面同步数据给父页面:@Link
下面示例中
1、父组件把clickIndex通过 Props 传递给子页面
2、点击子组件后,通过 Link 把修改后的clickIndex值传递给页面
3、其余子组件 Watch 了clickIndex,并同时修改组件中的isExpanded值
   页面Index.ets
  1. import TestItem from './TestItem';
  2. @Entry
  3. @Component
  4. struct Index {
  5.   private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  6.   @State clickIndex: number = -1;
  7.   build() {
  8.     Column() {
  9.       ForEach(this.arr, (item: number, index: number) => {
  10.         TestItem({
  11.           index: this.arr[index], //@Prop传递给子组件数据
  12.           clickIndex: $clickIndex, //@Link双向绑定数据
  13.         })
  14.       }, item => item)
  15.     }
  16.     .width('100%')
  17.     .height('100%')
  18.   }
  19. }
复制代码
  组件TestItem.ets
  1. @Component
  2. export default struct TestItem {
  3.   @Prop index: number; //当前 item 序号
  4.   @State isExpanded: boolean = false; //当前是否展开
  5.   // @Link修饰是为了同步数据到父组件,@Watch是为了监听回调给onClickIndexChanged
  6.   @Link @Watch('onClickIndexChanged') clickIndex: number; //点击的序号
  7.   onClickIndexChanged() {
  8.     this.isExpanded = this.clickIndex == this.index;
  9.   }
  10.   build() {
  11.     Button(this.index + '、是否展开:' + this.isExpanded)
  12.       .width('100%')
  13.       .height(this.isExpanded ? 80 : 40)
  14.       .fontSize(20)
  15.       .fontColor(Color.White)
  16.       .borderRadius(10)
  17.       .backgroundColor(0x007DFF)
  18.       .margin({ top: 10 })
  19.       .onClick(() => {
  20.         this.clickIndex = this.index;
  21.       })
  22.   }
  23. }
复制代码
2、子组件callback 回调父页面

   子组件声明callback 方法
  1. // 组件
  2. @Component
  3. export default struct TestItem {
  4.   callback?: (index: number) => void;
  5.   build() {
  6.     Button('子组件')
  7.       .width('100%')
  8.       .height(40)
  9.       .fontSize(20)
  10.       .fontColor(Color.White)
  11.       .borderRadius(10)
  12.       .backgroundColor(0x007DFF)
  13.       .onClick(() => {
  14.         // this.clickIndex = this.index;
  15.         if (this.callback !== undefined) {
  16.           this.callback(123)
  17.         }
  18.       })
  19.   }
  20. }
复制代码
  父页面传入callback方法
  1. TestItem({
  2.   callback: (index:number): void => {
  3.     console.warn("index:",index)
  4.   }
  5. })
复制代码
六、弹窗

1、告诫弹窗AlertDialog

  1. AlertDialog.show(
  2.   {
  3.     title: '删除联系人', // 标题
  4.     message: '是否需要删除所选联系人?', // 内容
  5.     autoCancel: false, // 点击遮障层时,是否关闭弹窗。
  6.     alignment: DialogAlignment.Bottom, // 弹窗在竖直方向的对齐方式
  7.     offset: { dx: 0, dy: -20 }, // 弹窗相对alignment位置的偏移量
  8.     primaryButton: {
  9.       value: '取消',
  10.       action: () => {
  11.       }
  12.     },
  13.     secondaryButton: {
  14.       value: '删除',
  15.       fontColor: '#D94838',
  16.       action: () => {
  17.       }
  18.     },
  19.     cancel: () => { // 点击遮障层关闭dialog时的回调
  20.     }
  21.   }
  22. )
复制代码
2、文本选择弹窗TextPickerDialog

  1. @State select: number = 2;
  2. private fruits: string[] = ['苹果', '橘子', '香蕉', '猕猴桃', '西瓜'];
  3. TextPickerDialog.show({
  4.   range: this.fruits, // 设置文本选择器的选择范围
  5.   selected: this.select, // 设置初始选中项的索引值。
  6.   onAccept: (value: TextPickerResult) => { // 点击弹窗中的“确定”按钮时触发该回调。
  7.     // 设置select为按下确定按钮时候的选中项index,这样当弹窗再次弹出时显示选中的是上一次确定的选项
  8.     this.select = value.index;
  9.     console.info("TextPickerDialog:onAccept()" + JSON.stringify(value));
  10.   },
  11.   onCancel: () => { // 点击弹窗中的“取消”按钮时触发该回调。
  12.     console.info("TextPickerDialog:onCancel()");
  13.   },
  14.   onChange: (value: TextPickerResult) => { // 滑动弹窗中的选择器使当前选中项改变时触发该回调。
  15.     console.info("TextPickerDialog:onChange()" + JSON.stringify(value));
  16.   }
  17. })
复制代码
3、日期滑动选择弹窗DatePickerDialog

  1. selectedDate: Date = new Date("2010-1-1")
  2. DatePickerDialog.show({
  3.   start: new Date("1900-1-1"), // 设置选择器的起始日期
  4.   end: new Date("2023-12-31"), // 设置选择器的结束日期
  5.   selected: this.selectedDate, // 设置当前选中的日期
  6.   lunar: false,
  7.   onAccept: (value: DatePickerResult) => { // 点击弹窗中的“确定”按钮时触发该回调
  8.     // 通过Date的setFullYear方法设置按下确定按钮时的日期,这样当弹窗再次弹出时显示选中的是上一次确定的日期
  9.     this.selectedDate.setFullYear(value.year, value.month, value.day)
  10.     console.info("DatePickerDialog:onAccept()" + JSON.stringify(value))
  11.   },
  12.   onCancel: () => { // 点击弹窗中的“取消”按钮时触发该回调
  13.     console.info("DatePickerDialog:onCancel()")
  14.   },
  15.   onChange: (value: DatePickerResult) => { // 滑动弹窗中的滑动选择器使当前选中项改变时触发该回调
  16.     console.info("DatePickerDialog:onChange()" + JSON.stringify(value))
  17.   }
  18. })
复制代码
4、自界说弹窗

   通过装饰器@CustomDialog界说的组件来实现,然后结合CustomDialogController来控制自界说弹窗的表现和隐蔽。
  
弹窗组件AddTargetDialog.ets绘制
  1. @CustomDialog
  2. export default struct AddTargetDialog {
  3.   @State subtaskName: string = '';
  4.   private controller?: CustomDialogController;
  5.   onClickOk?: (value: string) => void;
  6.   build() {
  7.     Column() {
  8.       Text('添加子目标')
  9.         .width('100%')
  10.         .fontSize('20fp')
  11.         .fontWeight(500)
  12.         .fontColor('#182431')
  13.         .textAlign(TextAlign.Start)
  14.       TextInput({ placeholder: '请输入子目标名称'})
  15.         .placeholderColor(Color.Grey)
  16.         .placeholderFont({ size: '16fp'})
  17.         .caretColor(Color.Blue)
  18.         .backgroundColor('#0D182431')
  19.         .width('100%')
  20.         .height('40%')
  21.         .margin({ top: '6%' })
  22.         .fontSize('16fp')
  23.         .fontColor("#182431")
  24.         .onChange((value: string) => {
  25.           this.subtaskName = value;
  26.         })
  27.       Blank()
  28.       Row() {
  29.         Button('取消')
  30.           .dialogButtonStyle()
  31.           .onClick(() => {
  32.             this.controller?.close();
  33.           })
  34.         Divider()
  35.           .vertical(true)
  36.         Button('确定')
  37.           .dialogButtonStyle()
  38.           .onClick(() => {
  39.             if (this.onClickOk !== undefined) {
  40.               this.onClickOk(this.subtaskName);
  41.             }
  42.           })
  43.       }
  44.       .width('70%')
  45.       .height('10%')
  46.       .justifyContent(FlexAlign.SpaceBetween)
  47.     }
  48.     .padding('24vp')
  49.     .height('168vp')
  50.     .width('90.3%')
  51.     .borderRadius(32)
  52.     .backgroundColor(Color.White)
  53.   }
  54. }
  55. /**
  56. * Custom button style.
  57. */
  58. @Extend(Button) function dialogButtonStyle() {
  59.   .fontSize('16fp')
  60.   .height('32vp')
  61.   .width('96vp')
  62.   .backgroundColor(Color.White)
  63.   .fontColor('#007DFF')
  64. }
复制代码
页面使用
  1. @Entry
  2. @Component
  3. struct Index {
  4.   dialogController: CustomDialogController = new CustomDialogController({
  5.     builder: AddTargetDialog({
  6.       onClickOk: (value: string): void => {
  7.         console.warn("value:",value)
  8.         this.dialogController.close();// 关闭
  9.       }
  10.     }),
  11.     alignment: DialogAlignment.Bottom,
  12.     offset: {
  13.       dx: 0,
  14.       dy: '-16vp'
  15.     },
  16.     customStyle: true,
  17.     autoCancel: false
  18.   });
  19.   build() {
  20.     Button("点击打开弹窗").onClick(()=>{
  21.       this.dialogController.open()// 打开
  22.     })
  23.   }
  24. }
复制代码
七、动画

添加animation属性就好,由State驱动。
  1. struct Index {
  2.   @State iconWidth: number = 30;
  3.   onPageShow() {
  4.     this.iconWidth = 90;
  5.   }
  6.   build() {
  7.     Column() {
  8.       Image($r('app.media.icon'))
  9.         .width(this.iconWidth)
  10.         .margin(10)
  11.         .objectFit(ImageFit.Contain)
  12.         .animation({
  13.           duration: 2000,
  14.           tempo: 3.0, //动画的播放速度
  15.           delay: 0,
  16.           curve: Curve.Linear,
  17.           playMode: PlayMode.Normal,
  18.           iterations: -1, //播放次数,默认一次,设置为-1时表示无限次播放。
  19.         })
  20.     }
  21.   }
  22. }
复制代码
八、网络请求

   注意:多个请求可以使用同一个httpRequest对象,httpRequest对象不能复用,由于它支持request、destroy、on和off方法,例如取消网络请求httpRequest.destroy();
  1. import http from '@ohos.net.http';
  2. let httpRequest = http.createHttp();
  3. let promise = httpRequest.request(
  4.   "http://www.baidu.com",
  5.   {
  6.     // 请求方式
  7.     method: http.RequestMethod.POST,
  8.     // 请求的额外数据。
  9.     extraData: {
  10.       "param1": "value1",
  11.       "param2": "value2",
  12.     },
  13.     // 可选,默认为60s
  14.     connectTimeout: 60000,
  15.     // 可选,默认为60s
  16.     readTimeout: 60000,
  17.     // 开发者根据自身业务需要添加header字段
  18.     header: {
  19.       'Content-Type': 'application/json'
  20.     }
  21.   });
  22. promise.then((data) => {
  23.   if (data.responseCode === http.ResponseCode.OK) {
  24.     console.info('Result:' + data.result);
  25.     console.info('code:' + data.responseCode);
  26.   }
  27. }).catch((err) => {
  28.   console.info('error:' + JSON.stringify(err));
  29. });
复制代码
九、路由

在如下目录下注册页面
  1. /entry/src/main/resources/base/profile/main_pages.json
复制代码
  跳转代码
  1. import router from '@ohos.router';
  2. router.pushUrl({
  3.   url: 'pages/SecondPage',
  4.   params: {
  5.     src: 'Index页面传来的数据',
  6.   }
  7. }, router.RouterMode.Single)
复制代码
鸿蒙参考资料

鸿蒙第一课视频,对应代码Codelabs
完整版的功能demo
官方文档
HarmonyOS点石成金
鸿蒙体系系列教程6-鸿蒙体系项目结构分析
鸿蒙开辟者学习笔记

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

道家人

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表