HarmonyOS Next开辟学习手册——同层渲染绘制Video和Button组件 ...

打印 上一主题 下一主题

主题 980|帖子 980|积分 2940

同层渲染是ArkWeb组件为应用提供原生组件和Web元素渲染在同一层级的本领。支持的组件范围请参考 NodeRenderType 说明。


  • 使用前请在module.json5中添加网络权限,添加方法请参考 在配置文件中声明权限 。
  1. "requestPermissions":[
  2.     {
  3.       "name" : "ohos.permission.INTERNET"
  4.     }
  5.   ]
复制代码
约束限制

使用同层渲染的功能时会有如下限制。


  • 不支持W3C规格尺度标签定义为同层标签。
  • 不支持同时配置Object标签和Embed标签作为同层渲染标签。
  • 一个页面内同层标签的个数发起不凌驾五个,凌驾这个范围性能体验大概得不到保障。
  • 同层标签最大高度不凌驾8192px,最大纹理巨细为8192px。
  • Web组件嵌套Web只支持一层嵌套,不支持多层嵌套。如果多层嵌套,会表现 “该插件不受支持”。
  • 同层渲染区域支持的触屏事故包括:滑动、点击、缩放、长按,不支持拖拽。
  • 同层渲染区域不支持鼠标、键盘、触摸板事故
  • 开启同层渲染后,Web组件打开的所有Web页面将不支持同一渲染模式 RenderMode 。
绘制XComponent+AVPlayer和Button组件

使能同层渲染模式

开辟者可通过 enableNativeEmbedMode() 控制同层渲染开关。Html文件中须要显式使用embed标签,而且embed标签内type必须以“native/”开头。同层标签对应的元素区域的背景为透明。


  • 应用侧代码组件使用示例。
  1. // HAP's src/main/ets/pages/Index.ets
  2. // 创建NodeController
  3. import { webview } from '@kit.ArkWeb';
  4. import { UIContext, NodeController, BuilderNode, NodeRenderType, FrameNode } from "@kit.ArkUI";
  5. import { AVPlayerDemo } from './PlayerDemo';
  6. @Observed
  7. declare class Params {
  8.   textOne : string
  9.   textTwo : string
  10.   width : number
  11.   height : number
  12. }
  13. declare class nodeControllerParams {
  14.   surfaceId : string
  15.   type : string
  16.   renderType : NodeRenderType
  17.   embedId : string
  18.   width : number
  19.   height : number
  20. }
  21. // 用于控制和反馈对应的NodeContainer上的节点的行为,需要与NodeContainer一起使用。
  22. class MyNodeController extends NodeController {
  23.   private rootNode: BuilderNode<[Params]> | undefined | null;
  24.   private embedId_ : string = "";
  25.   private surfaceId_ : string = "";
  26.   private renderType_ :NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
  27.   private width_ : number = 0;
  28.   private height_ : number = 0;
  29.   private type_ : string = "";
  30.   private isDestroy_ : boolean = false;
  31.   setRenderOption(params : nodeControllerParams) {
  32.     this.surfaceId_ = params.surfaceId;
  33.     this.renderType_ = params.renderType;
  34.     this.embedId_ = params.embedId;
  35.     this.width_ = params.width;
  36.     this.height_ = params.height;
  37.     this.type_ = params.type;
  38.   }
  39.   // 必须要重写的方法,用于构建节点数、返回节点数挂载在对应NodeContainer中。
  40.   // 在对应NodeContainer创建的时候调用、或者通过rebuild方法调用刷新。
  41.   makeNode(uiContext: UIContext): FrameNode | null{
  42.     if (this.isDestroy_) { // rootNode为null
  43.       return null;
  44.     }
  45.     if (!this.rootNode) { // rootNode 为undefined时
  46.       this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_});
  47.       if (this.type_ === 'native/video') {
  48.         this.rootNode.build(wrapBuilder(VideoBuilder), {textOne: "myButton", width : this.width_, height : this.height_});
  49.       } else {
  50.         // other
  51.       }
  52.     }
  53.     // 返回FrameNode节点。
  54.     return this.rootNode.getFrameNode();
  55.   }
  56.   setBuilderNode(rootNode: BuilderNode<Params[]> | null): void{
  57.     this.rootNode = rootNode;
  58.   }
  59.   getBuilderNode(): BuilderNode<[Params]> | undefined | null{
  60.     return this.rootNode;
  61.   }
  62.   updateNode(arg: Object): void {
  63.     this.rootNode?.update(arg);
  64.   }
  65.   getEmbedId() : string {
  66.     return this.embedId_;
  67.   }
  68.   setDestroy(isDestroy : boolean) : void {
  69.     this.isDestroy_ = isDestroy;
  70.     if (this.isDestroy_) {
  71.       this.rootNode = null;
  72.     }
  73.   }
  74.   postEvent(event: TouchEvent | undefined) : boolean {
  75.     return this.rootNode?.postTouchEvent(event) as boolean
  76.   }
  77. }
  78. @Component
  79. struct VideoComponent {
  80.   @ObjectLink params: Params
  81.   @State bkColor: Color = Color.Red
  82.   mXComponentController: XComponentController = new XComponentController();
  83.   @State player_changed: boolean = false;
  84.   player?: AVPlayerDemo;
  85.   build() {
  86.     Column() {
  87.       Button(this.params.textOne)
  88.       XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController})
  89.         .border({width: 1, color: Color.Red})
  90.         .onLoad(() => {
  91.           this.player = new AVPlayerDemo();
  92.           this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId());
  93.           this.player_changed = !this.player_changed;
  94.           this.player.avPlayerLiveDemo()
  95.         })
  96.         .width(300)
  97.         .height(200)
  98.     }
  99.     //自定义组件中的最外层容器组件宽高应该为同层标签的宽高
  100.     .width(this.params.width)
  101.     .height(this.params.height)
  102.   }
  103. }
  104. // @Builder中为动态组件的具体组件内容。
  105. @Builder
  106. function VideoBuilder(params: Params) {
  107.   VideoComponent({ params: params })
  108.     .backgroundColor(Color.Gray)
  109. }
  110. @Entry
  111. @Component
  112. struct WebIndex {
  113.   browserTabController: WebviewController = new webview.WebviewController()
  114.   private nodeControllerMap: Map<string, MyNodeController> = new Map();
  115.   @State componentIdArr: Array<string> = [];
  116.   aboutToAppear() {
  117.     // 配置web开启调试模式。
  118.     webview.WebviewController.setWebDebuggingAccess(true);
  119.   }
  120.   build(){
  121.     Row() {
  122.       Column() {
  123.         Stack() {
  124.           ForEach(this.componentIdArr, (componentId: string) => {
  125.             NodeContainer(this.nodeControllerMap.get(componentId))
  126.           }, (embedId: string) => embedId)
  127.           // Web组件加载本地test.html页面。
  128.           Web({ src: $rawfile("test.html"), controller: this.browserTabController })
  129.               // 配置同层渲染开关开启。
  130.             .enableNativeEmbedMode(true)
  131.               // 获取embed标签的生命周期变化数据。
  132.             .onNativeEmbedLifecycleChange((embed) => {
  133.               console.log("NativeEmbed surfaceId" + embed.surfaceId);
  134.               // 1. 如果使用embed.info.id作为映射nodeController的key,请在h5页面显式指定id
  135.               const componentId = embed.info?.id?.toString() as string
  136.               if (embed.status == NativeEmbedStatus.CREATE) {
  137.                 console.log("NativeEmbed create" + JSON.stringify(embed.info))
  138.                 // 创建节点控制器,设置参数并rebuild。
  139.                 let nodeController = new MyNodeController()
  140.                 // 1. embed.info.width和embed.info.height单位是px格式,需要转换成ets侧的默认单位vp
  141.                 nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string,
  142.                   renderType : NodeRenderType.RENDER_TYPE_TEXTURE, embedId : embed.embedId as string,
  143.                   width : px2vp(embed.info?.width), height : px2vp(embed.info?.height)})
  144.                 nodeController.setDestroy(false);
  145.                 // 根据web传入的embed的id属性作为key,将nodeController存入map。
  146.                 this.nodeControllerMap.set(componentId, nodeController)
  147.                 // 将web传入的embed的id属性存入@State状态数组变量中,用于动态创建nodeContainer节点容器,需要将push动作放在set之后。
  148.                 this.componentIdArr.push(componentId)
  149.               } else if (embed.status == NativeEmbedStatus.UPDATE) {
  150.                 let nodeController = this.nodeControllerMap.get(componentId)
  151.                 nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
  152.               } else {
  153.                 let nodeController = this.nodeControllerMap.get(componentId);
  154.                 nodeController?.setDestroy(true)
  155.                 this.nodeControllerMap.clear();
  156.                 this.componentIdArr.length = 0;
  157.               }
  158.             })// 获取同层渲染组件触摸事件信息。
  159.             .onNativeEmbedGestureEvent((touch) => {
  160.               console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
  161.               this.componentIdArr.forEach((componentId: string) => {
  162.                 let nodeController = this.nodeControllerMap.get(componentId)
  163.                 // 将获取到的同层区域的事件发送到该区域embedId对应的nodeController上
  164.                 if (nodeController?.getEmbedId() === touch.embedId) {
  165.                   let ret = nodeController?.postEvent(touch.touchEvent)
  166.                   if (ret) {
  167.                     console.log("onNativeEmbedGestureEvent success " + componentId)
  168.                   } else {
  169.                     console.log("onNativeEmbedGestureEvent fail " + componentId)
  170.                   }
  171.                   if (touch.result) {
  172.                     // 通知Web组件手势事件消费结果
  173.                     touch.result.setGestureEventResult(ret);
  174.                   }
  175.                 }
  176.               })
  177.             })
  178.         }
  179.       }
  180.     }
  181.   }
  182. }
复制代码


  • 应用侧代码,视频播放示例。
  1. // HAP's src/main/ets/pages/PlayerDemo.ets
  2. import { media } from '@kit.MediaKit';
  3. import { BusinessError } from '@ohos.base';
  4. export class AVPlayerDemo {
  5.   private count: number = 0;
  6.   private surfaceID: string = ''; // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接见上面Xcomponent创建方法。
  7.   private isSeek: boolean = true; // 用于区分模式是否支持seek操作。
  8.   setSurfaceID(surface_id: string){
  9.     console.log('setSurfaceID : ' + surface_id);
  10.     this.surfaceID = surface_id;
  11.   }
  12.   // 注册avplayer回调函数。
  13.   setAVPlayerCallback(avPlayer: media.AVPlayer) {
  14.     // seek操作结果回调函数。
  15.     avPlayer.on('seekDone', (seekDoneTime: number) => {
  16.       console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
  17.     })
  18.     // error回调监听函数,当avplayer在操作过程中出现错误时,调用reset接口触发重置流程。
  19.     avPlayer.on('error', (err: BusinessError) => {
  20.       console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
  21.       avPlayer.reset();
  22.     })
  23.     // 状态机变化回调函数。
  24.     avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
  25.       switch (state) {
  26.         case 'idle': // 成功调用reset接口后触发该状态机上报。
  27.           console.info('AVPlayer state idle called.');
  28.           avPlayer.release(); // 调用release接口销毁实例对象。
  29.           break;
  30.         case 'initialized': // avplayer 设置播放源后触发该状态上报。
  31.           console.info('AVPlayer state initialized called.');
  32.           avPlayer.surfaceId = this.surfaceID; // 设置显示画面,当播放的资源为纯音频时无需设置。
  33.           avPlayer.prepare();
  34.           break;
  35.         case 'prepared': // prepared调用成功后上报该状态机。
  36.           console.info('AVPlayer state prepared called.');
  37.           avPlayer.play(); // 调用播放接口开始播放。
  38.           break;
  39.         case 'playing': // play成功调用后触发该状态机上报。
  40.           console.info('AVPlayer state prepared called.');
  41.           if(this.count !== 0) {
  42.             if (this.isSeek) {
  43.               console.info('AVPlayer start to seek.');
  44.               avPlayer.seek(avPlayer.duration); // seek到视频末尾。
  45.             } else {
  46.               // 当播放模式不支持seek操作时继续播放到结尾。
  47.               console.info('AVPlayer wait to play end.');
  48.             }
  49.           } else {
  50.             avPlayer.pause(); // 调用暂停接口暂停播放。
  51.           }
  52.           this.count++;
  53.           break;
  54.         case 'paused': // pause成功调用后触发该状态机上报。
  55.           console.info('AVPlayer state paused called.');
  56.           avPlayer.play(); // 再次播放接口开始播放。
  57.           break;
  58.         case 'completed': //播放接口后触发该状态机上报。
  59.           console.info('AVPlayer state paused called.');
  60.           avPlayer.stop(); // 调用播放接口接口。
  61.           break;
  62.         case 'stopped': // stop接口后触发该状态机上报。
  63.           console.info('AVPlayer state stopped called.');
  64.           avPlayer.reset(); // 调用reset接口初始化avplayer状态。
  65.           break;
  66.         case 'released': //播放接口后触发该状态机上报。
  67.           console.info('AVPlayer state released called.');
  68.           break;
  69.         default:
  70.           break;
  71.       }
  72.     })
  73.   }
  74.   // 通过url设置网络地址来实现播放直播码流。
  75.   async avPlayerLiveDemo(){
  76.     // 创建avPlayer实例对象
  77.     let avPlayer: media.AVPlayer = await media.createAVPlayer();
  78.     // 创建状态机变化回调函数。
  79.     this.setAVPlayerCallback(avPlayer);
  80.     this.isSeek = false; // 不支持seek操作。
  81.     // 使用时需要自行替换视频链接
  82.     avPlayer.url = 'https://xxx.xxx/demo.mp4';
  83.   }
  84. }
复制代码


  • 前端页面示例。
  1. <!--HAP's src/main/resources/rawfile/test.html-->
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5.     <title>同层渲染测试html</title>
  6.     <meta name="viewport">
  7. </head>
  8. <body>
  9. <div>
  10.     <div id="bodyId">
  11.         <embed id="nativeVideo" type = "native/video" width="1000" height="1500" src="test" style = "background-color:red"/>
  12.     </div>
  13. </div>
  14. </body>
  15. </html>
复制代码

使能同层渲染模式并指定标署名和自定义范例

开辟者也可通过 registerNativeEmbedRule(tag: string, type: string) 指定tag标签和自定义范例。
当前tag仅支持"embed"和"object",type范例则可任意指定,两个字符串参数均不区分巨细写,ArkWeb内核侧将会同一转成小写,其中tag字串使用全字符串匹配,type使用字符串前缀匹配。
若开辟者不使用该接口或该接口吸收的为非法字符串(如:空字符串)时,内核将使用默认设置即"embed" + "native/"前缀模式,若指定范例与w3c定义的object或embed尺度范例重合如registerNativeEmbedRule(“object”, “application/pdf”),
ArkWeb将遵循w3c尺度举动,不会将其识别为同层标签。


  • 应用侧代码使用registerNativeEmbedRule示例。
  1. class MyNodeController extends NodeController {
  2.   ...
  3.   makeNode(uiContext: UIContext): FrameNode | null{
  4.     if (this.type_ === 'test') {
  5.       ...
  6.     } else if (this.type_ === 'test/video') {
  7.       ...
  8.     } else {
  9.       // other
  10.     }
  11.     ...
  12.   }
  13.   ...
  14. }
  15. ...
  16.   build(){
  17.       ...
  18.         Stack() {
  19.           ...
  20.           Web({ src: $rawfile("test.html"), controller: this.browserTabController })
  21.              // 配置同层渲染开关开启。
  22.             .enableNativeEmbedMode(true)
  23.              // 注册同层标签为"object",类型为"test"前缀。
  24.             .registerNativeEmbedRule("object", "test")
  25.             ...
  26.         }
  27.       ...
  28.   }
复制代码


  • 与registerNativeEmbedRule相对应的前端页面代码,范例可使用"test"及以"test"为前缀的字串。
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>同层渲染测试html</title>
  5.     <meta name="viewport">
  6. </head>
  7. <body>
  8. <div>
  9.     <div>
  10.         <object id="nativeButton" type="test" width="800" height="800" data="test?params1=xxx?" style = "background-color:red"/>
  11.           <param name="id" value="playerId" />
  12.           <param name="data" value='{}' />
  13.         </object>
  14.     </div>
  15.     <div>
  16.         <object id="nativeVideo" type="test/video" width="500" height="500" data="test" style = "background-color:red"/><object>
  17.     </div>
  18. </div>
  19. <div id="button" width="500" height="200">
  20.     <p>bottom</p>
  21. </div>
  22. </body>
  23. </html>
复制代码
绘制TextInput组件并将同层元素更新时上报的位置信息更新到组件侧

触发同层元素更新的举动包括滚动、缩放、元素发生改变导致的重排等。由于同层元素的位置基于Web组件坐标系,对于网页缩放这种并未真正改变元素的size的举动,只会有position的改变,宽高仍保持初始值。
须要位置信息的组件如TextInput、TextArea等需将同层元素更新上报来的位置信息实时更新到组件侧。


  • 应用侧完整示例。
  1. ...
  2. class MyNodeController extends NodeController {
  3.   ...
  4.   makeNode(uiContext: UIContext): FrameNode | null{
  5.     if (this.type_ === 'application/view') {
  6.       this.rootNode.build(wrapBuilder(TextInputBuilder), {
  7.         textOne: "myInput",
  8.         width: this.width_,
  9.         height: this.height_
  10.       });
  11.     } else {
  12.       // other
  13.     }
  14.     ...
  15.   }
  16.   ...
  17. }
  18. @Component
  19. struct TextInputComponent {
  20.   @Prop params: Params
  21.   @State bkColor: Color = Color.Red
  22.   mXComponentController: XComponentController = new XComponentController();
  23.   build() {
  24.     Column() {
  25.       TextInput({ text: `${this.params.textOne}` })
  26.         .height(50)
  27.         .width(200)
  28.         .backgroundColor(Color.Green)
  29.         .onTouch((event) => {
  30.           console.log('input1 event ' + JSON.stringify(event));
  31.         }).margin({ top: 30})
  32.       TextInput({ text: `${this.params.textOne}` })
  33.         .height(50)
  34.         .width(200)
  35.         .backgroundColor(Color.Green)
  36.         .onTouch((event) => {
  37.           console.log('input2 event ' + JSON.stringify(event));
  38.         }).margin({ top: 30})
  39.       TextInput({ text: `${this.params.textOne}` })
  40.         .height(50)
  41.         .width(200)
  42.         .backgroundColor(Color.Green)
  43.         .onTouch((event) => {
  44.           console.log('input2 event ' + JSON.stringify(event));
  45.         }).margin({ top: 30})
  46.     }
  47.     .width(this.params.width)
  48.     .height(this.params.height)
  49.   }
  50. }
  51. @Builder
  52. function TextInputBuilder(params: Params) {
  53.   TextInputComponent({ params: params })
  54.     .height(params.height)
  55.     .width(params.width)
  56.     .backgroundColor(Color.Red)
  57. }
  58. @Entry
  59. @Component
  60. struct Page {
  61.   browserTabController: WebviewController = new webview.WebviewController()
  62.   private nodeControllerMap: Map<string, MyNodeController> = new Map();
  63.   @State componentIdArr: Array<string> = [];
  64.   @State edges: Edges = {};
  65.   build() {
  66.     Row() {
  67.       Column() {
  68.         Stack(){
  69.           ForEach(this.componentIdArr, (componentId: string) => {
  70.             NodeContainer(this.nodeControllerMap.get(componentId)).position(this.edges)
  71.           }, (embedId: string) => embedId)
  72.           Web({ src: $rawfile('test.html'), controller: this.browserTabController})
  73.             .enableNativeEmbedMode(true)
  74.             .registerNativeEmbedRule("object", "APPlication/view")
  75.             .onNativeEmbedLifecycleChange((embed) => {
  76.               const componentId = embed.info?.id?.toString() as string;
  77.               if (embed.status == NativeEmbedStatus.CREATE) {
  78.                 // 建议用edges的方式使用position,避免px和vp的转换出现浮点数运算带来额外的精度损失
  79.                 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
  80.                 let nodeController = new MyNodeController()
  81.                 nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
  82.                   type : embed.info?.type as string,
  83.                   renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
  84.                   embedId : embed.embedId as string,
  85.                   width : px2vp(embed.info?.width),
  86.                   height :px2vp(embed.info?.height)})
  87.                 nodeController.rebuild()
  88.                 this.nodeControllerMap.set(componentId, nodeController)
  89.                 this.componentIdArr.push(componentId)
  90.               } else if (embed.status == NativeEmbedStatus.UPDATE) {
  91.                 console.log("NativeEmbed update" + JSON.stringify(embed.info))
  92.                 this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
  93.                 let nodeController = this.nodeControllerMap.get(componentId)
  94.                 nodeController?.updateNode({text: 'update',   width : px2vp(embed.info?.width),
  95.                   height :px2vp(embed.info?.height)} as ESObject)
  96.                 nodeController?.rebuild()
  97.               } else {
  98.                 let nodeController = this.nodeControllerMap.get(componentId)
  99.                 nodeController?.setBuilderNode(null)
  100.                 nodeController?.rebuild()
  101.               }
  102.             })
  103.             .onNativeEmbedGestureEvent((touch) => {
  104.               this.componentIdArr.forEach((componentId: string) => {
  105.                 let nodeController = this.nodeControllerMap.get(componentId)
  106.                 if (nodeController?.getEmbedId() === touch.embedId) {
  107.                   let ret = nodeController?.postEvent(touch.touchEvent)
  108.                   if (ret) {
  109.                     console.log("onNativeEmbedGestureEvent success " + componentId)
  110.                   } else {
  111.                     console.log("onNativeEmbedGestureEvent fail " + componentId)
  112.                   }
  113.                 }
  114.               })
  115.             })
  116.         }
  117.       }
  118.       .width('100%')
  119.     }
  120.     .height('100%')
  121.   }
  122. }
复制代码


  • 前述应用侧相对应的前端示例。
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>同层渲染测试html</title>
  5.     <meta charset="UTF-8">
  6.     <style>
  7.     html {
  8.         background-color: blue;
  9.     }
  10.     </style>
  11. </head>
  12. <body>
  13. <div id="bodyId" style="width:800px; height:1000px; margin-top:1000px;">
  14.     <object id="cameraTest" type="application/view" width="100%" height="100%" ></object>
  15. </div>
  16. <div style="height:1000px;">
  17. </div>
  18. </body>
  19. </html>
复制代码
鸿蒙全栈开辟全新学习指南

有许多小搭档不知道学习哪些鸿蒙开辟技术?不知道须要重点掌握哪些鸿蒙应用开辟知识点?而且学习时频繁踩坑,最终浪费大量时间。以是要有一份实用的鸿蒙(HarmonyOS NEXT)学习门路与学习文档用来跟着学习黑白常有须要的。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开辟技术的学习门路,包含了鸿蒙开辟必掌握的焦点知识要点,内容有(ArkTS、ArkUI开辟组件、Stage模型、多端部署、分布式应用开辟、WebGL、元服务、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开辟、系统定制移植等等)鸿蒙(HarmonyOS NEXT)技术知识点。
本门路共分为四个阶段:

第一阶段:鸿蒙初中级开辟必备技能


第二阶段:鸿蒙南北双向高工技能根本:gitee.com/MNxiaona/733GH


第三阶段:应用开辟中高级就业技术


第四阶段:全网首发-工业级南向装备开辟就业技术:gitee.com/MNxiaona/733GH


《鸿蒙 (Harmony OS)开辟学习手册》(共计892页)

怎样快速入门?

1.根本概念
2.构建第一个ArkTS应用
3.……

开辟根本知识:gitee.com/MNxiaona/733GH

1.应用根本知识
2.配置文件
3.应用数据管理
4.应用安全管理
5.应用隐私掩护
6.三方应用调用管控机制
7.资源分类与访问
8.学习ArkTS语言
9.……

基于ArkTS 开辟

1.Ability开辟
2.UI开辟
3.公共事故与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.装备管理
12.装备使用信息统计
13.DFX
14.国际化开辟
15.折叠屏系列
16.……

鸿蒙开辟面试真题(含参考答案):gitee.com/MNxiaona/733GH


鸿蒙入门教学视频:


美团APP实战开辟教学:gitee.com/MNxiaona/733GH


写在最后



  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以等待后续文章ing

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

小秦哥

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表