首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-02-22 07:01
  • 4353
  • 13997
blog.csdn.net

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

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

约束限制

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

  • 不支持W3C规格标准标签定义为同层标签。

  • 不支持同时配置Object标签和Embed标签作为同层渲染标签。

  • 一个页面内同层标签的个数建议不超过五个,超过这个范围性能体验可能得不到保障。

  • 同层标签最大高度不超过8192px,最大纹理大小为8192px。

  • Web组件嵌套Web只支持一层嵌套,不支持多层嵌套。如果多层嵌套,会显示 “该插件不受支持”。

  • 同层渲染区域支持的触屏事件包括:滑动、点击、缩放、长按,不支持拖拽。

  • 同层渲染区域不支持鼠标、键盘、触摸板事件

  • 开启同层渲染后,Web组件打开的所有Web页面将不支持统一渲染模式 RenderMode 。

绘制XComponent+AVPlayer和Button组件

使能同层渲染模式

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

  • 应用侧代码组件使用示例。
// HAP's src/main/ets/pages/Index.ets
// 创建NodeController
import { webview } from '@kit.ArkWeb';
import { UIContext, NodeController, BuilderNode, NodeRenderType, FrameNode } from "@kit.ArkUI";
import { AVPlayerDemo } from './PlayerDemo';

@Observed
declare class Params {
  textOne : string
  textTwo : string
  width : number
  height : number
}

declare class nodeControllerParams {
  surfaceId : string
  type : string
  renderType : NodeRenderType
  embedId : string
  width : number
  height : number
}

// 用于控制和反馈对应的NodeContainer上的节点的行为,需要与NodeContainer一起使用。
class MyNodeController extends NodeController {
  private rootNode: BuilderNode<[Params]> | undefined | null;
  private embedId_ : string = "";
  private surfaceId_ : string = "";
  private renderType_ :NodeRenderType = NodeRenderType.RENDER_TYPE_DISPLAY;
  private width_ : number = 0;
  private height_ : number = 0;
  private type_ : string = "";
  private isDestroy_ : boolean = false;

  setRenderOption(params : nodeControllerParams) {
    this.surfaceId_ = params.surfaceId;
    this.renderType_ = params.renderType;
    this.embedId_ = params.embedId;
    this.width_ = params.width;
    this.height_ = params.height;
    this.type_ = params.type;
  }
  // 必须要重写的方法,用于构建节点数、返回节点数挂载在对应NodeContainer中。
  // 在对应NodeContainer创建的时候调用、或者通过rebuild方法调用刷新。
  makeNode(uiContext: UIContext): FrameNode | null{
    if (this.isDestroy_) { // rootNode为null
      return null;
    }
    if (!this.rootNode) { // rootNode 为undefined时
      this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId_, type: this.renderType_});
      if (this.type_ === 'native/video') {
        this.rootNode.build(wrapBuilder(VideoBuilder), {textOne: "myButton", width : this.width_, height : this.height_});
      } else {
        // other
      }
    }
    // 返回FrameNode节点。
    return this.rootNode.getFrameNode();
  }

  setBuilderNode(rootNode: BuilderNode | null): void{
    this.rootNode = rootNode;
  }

  getBuilderNode(): BuilderNode<[Params]> | undefined | null{
    return this.rootNode;
  }

  updateNode(arg: Object): void {
    this.rootNode?.update(arg);
  }
  getEmbedId() : string {
    return this.embedId_;
  }

  setDestroy(isDestroy : boolean) : void {
    this.isDestroy_ = isDestroy;
    if (this.isDestroy_) {
      this.rootNode = null;
    }
  }

  postEvent(event: TouchEvent | undefined) : boolean {
    return this.rootNode?.postTouchEvent(event) as boolean
  }
}

@Component
struct VideoComponent {
  @ObjectLink params: Params
  @State bkColor: Color = Color.Red
  mXComponentController: XComponentController = new XComponentController();
  @State player_changed: boolean = false;
  player?: AVPlayerDemo;

  build() {
    Column() {
      Button(this.params.textOne)

      XComponent({ id: 'video_player_id', type: XComponentType.SURFACE, controller: this.mXComponentController})
        .border({width: 1, color: Color.Red})
        .onLoad(() => {
          this.player = new AVPlayerDemo();
          this.player.setSurfaceID(this.mXComponentController.getXComponentSurfaceId());
          this.player_changed = !this.player_changed;
          this.player.avPlayerLiveDemo()
        })
        .width(300)
        .height(200)
    }
    //自定义组件中的最外层容器组件宽高应该为同层标签的宽高
    .width(this.params.width)
    .height(this.params.height)
  }
}
// @Builder中为动态组件的具体组件内容。
@Builder
function VideoBuilder(params: Params) {
  VideoComponent({ params: params })
    .backgroundColor(Color.Gray)
}

@Entry
@Component
struct WebIndex {
  browserTabController: WebviewController = new webview.WebviewController()
  private nodeControllerMap: Map = new Map();
  @State componentIdArr: Array = [];

  aboutToAppear() {
    // 配置web开启调试模式。
    webview.WebviewController.setWebDebuggingAccess(true);
  }

  build(){
    Row() {
      Column() {
        Stack() {
          ForEach(this.componentIdArr, (componentId: string) => {
            NodeContainer(this.nodeControllerMap.get(componentId))
          }, (embedId: string) => embedId)
          // Web组件加载本地test.html页面。
          Web({ src: $rawfile("test.html"), controller: this.browserTabController })
              // 配置同层渲染开关开启。
            .enableNativeEmbedMode(true)
              // 获取embed标签的生命周期变化数据。
            .onNativeEmbedLifecycleChange((embed) => {
              console.log("NativeEmbed surfaceId" + embed.surfaceId);
              // 1. 如果使用embed.info.id作为映射nodeController的key,请在h5页面显式指定id
              const componentId = embed.info?.id?.toString() as string
              if (embed.status == NativeEmbedStatus.CREATE) {
                console.log("NativeEmbed create" + JSON.stringify(embed.info))
                // 创建节点控制器,设置参数并rebuild。
                let nodeController = new MyNodeController()
                // 1. embed.info.width和embed.info.height单位是px格式,需要转换成ets侧的默认单位vp
                nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string,
                  renderType : NodeRenderType.RENDER_TYPE_TEXTURE, embedId : embed.embedId as string,
                  width : px2vp(embed.info?.width), height : px2vp(embed.info?.height)})
                nodeController.setDestroy(false);
                // 根据web传入的embed的id属性作为key,将nodeController存入map。
                this.nodeControllerMap.set(componentId, nodeController)
                // 将web传入的embed的id属性存入@State状态数组变量中,用于动态创建nodeContainer节点容器,需要将push动作放在set之后。
                this.componentIdArr.push(componentId)
              } else if (embed.status == NativeEmbedStatus.UPDATE) {
                let nodeController = this.nodeControllerMap.get(componentId)
                nodeController?.updateNode({textOne: 'update', width: px2vp(embed.info?.width), height: px2vp(embed.info?.height)} as ESObject)
              } else {
                let nodeController = this.nodeControllerMap.get(componentId);
                nodeController?.setDestroy(true)
                this.nodeControllerMap.clear();
                this.componentIdArr.length = 0;
              }
            })// 获取同层渲染组件触摸事件信息。
            .onNativeEmbedGestureEvent((touch) => {
              console.log("NativeEmbed onNativeEmbedGestureEvent" + JSON.stringify(touch.touchEvent));
              this.componentIdArr.forEach((componentId: string) => {
                let nodeController = this.nodeControllerMap.get(componentId)
                // 将获取到的同层区域的事件发送到该区域embedId对应的nodeController上
                if (nodeController?.getEmbedId() === touch.embedId) {
                  let ret = nodeController?.postEvent(touch.touchEvent)
                  if (ret) {
                    console.log("onNativeEmbedGestureEvent success " + componentId)
                  } else {
                    console.log("onNativeEmbedGestureEvent fail " + componentId)
                  }
                  if (touch.result) {
                    // 通知Web组件手势事件消费结果
                    touch.result.setGestureEventResult(ret);
                  }
                }
              })
            })
        }
      }
    }
  }
}
  • 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
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 应用侧代码,视频播放示例。
// HAP's src/main/ets/pages/PlayerDemo.ets
import { media } from '@kit.MediaKit';
import { BusinessError } from '@ohos.base';

export class AVPlayerDemo {
  private count: number = 0;
  private surfaceID: string = ''; // surfaceID用于播放画面显示,具体的值需要通过Xcomponent接口获取,相关文档链接见上面Xcomponent创建方法。
  private isSeek: boolean = true; // 用于区分模式是否支持seek操作。

  setSurfaceID(surface_id: string){
    console.log('setSurfaceID : ' + surface_id);
    this.surfaceID = surface_id;
  }
  // 注册avplayer回调函数。
  setAVPlayerCallback(avPlayer: media.AVPlayer) {
    // seek操作结果回调函数。
    avPlayer.on('seekDone', (seekDoneTime: number) => {
      console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
    })
    // error回调监听函数,当avplayer在操作过程中出现错误时,调用reset接口触发重置流程。
    avPlayer.on('error', (err: BusinessError) => {
      console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);
      avPlayer.reset();
    })
    // 状态机变化回调函数。
    avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
      switch (state) {
        case 'idle': // 成功调用reset接口后触发该状态机上报。
          console.info('AVPlayer state idle called.');
          avPlayer.release(); // 调用release接口销毁实例对象。
          break;
        case 'initialized': // avplayer 设置播放源后触发该状态上报。
          console.info('AVPlayer state initialized called.');
          avPlayer.surfaceId = this.surfaceID; // 设置显示画面,当播放的资源为纯音频时无需设置。
          avPlayer.prepare();
          break;
        case 'prepared': // prepared调用成功后上报该状态机。
          console.info('AVPlayer state prepared called.');
          avPlayer.play(); // 调用播放接口开始播放。
          break;
        case 'playing': // play成功调用后触发该状态机上报。
          console.info('AVPlayer state prepared called.');
          if(this.count !== 0) {
            if (this.isSeek) {
              console.info('AVPlayer start to seek.');
              avPlayer.seek(avPlayer.duration); // seek到视频末尾。
            } else {
              // 当播放模式不支持seek操作时继续播放到结尾。
              console.info('AVPlayer wait to play end.');
            }
          } else {
            avPlayer.pause(); // 调用暂停接口暂停播放。
          }
          this.count++;
          break;
        case 'paused': // pause成功调用后触发该状态机上报。
          console.info('AVPlayer state paused called.');
          avPlayer.play(); // 再次播放接口开始播放。
          break;
        case 'completed': //播放接口后触发该状态机上报。
          console.info('AVPlayer state paused called.');
          avPlayer.stop(); // 调用播放接口接口。
          break;
        case 'stopped': // stop接口后触发该状态机上报。
          console.info('AVPlayer state stopped called.');
          avPlayer.reset(); // 调用reset接口初始化avplayer状态。
          break;
        case 'released': //播放接口后触发该状态机上报。
          console.info('AVPlayer state released called.');
          break;
        default:
          break;
      }
    })
  }

  // 通过url设置网络地址来实现播放直播码流。
  async avPlayerLiveDemo(){
    // 创建avPlayer实例对象
    let avPlayer: media.AVPlayer = await media.createAVPlayer();
    // 创建状态机变化回调函数。
    this.setAVPlayerCallback(avPlayer);
    this.isSeek = false; // 不支持seek操作。
    // 使用时需要自行替换视频链接
    avPlayer.url = 'https://xxx.xxx/demo.mp4';
  }
}
  • 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
  • 前端页面示例。




    同层渲染测试html
    


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

使能同层渲染模式并指定标签名和自定义类型

开发者也可通过 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示例。
class MyNodeController extends NodeController {
  ...
  makeNode(uiContext: UIContext): FrameNode | null{

    if (this.type_ === 'test') {
      ...
    } else if (this.type_ === 'test/video') {
      ...
    } else {
      // other
    }
    ...
  }
  ...
}
...

  build(){
      ...
        Stack() {
          ...
          Web({ src: $rawfile("test.html"), controller: this.browserTabController })
             // 配置同层渲染开关开启。
            .enableNativeEmbedMode(true)
             // 注册同层标签为"object",类型为"test"前缀。
            .registerNativeEmbedRule("object", "test")
            ...
        }
      ...
  }
  • 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
  • 与registerNativeEmbedRule相对应的前端页面代码,类型可使用"test"及以"test"为前缀的字串。



    同层渲染测试html
    


bottom

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

绘制TextInput组件并将同层元素更新时上报的位置信息更新到组件侧

触发同层元素更新的行为包括滚动、缩放、元素发生改变导致的重排等。由于同层元素的位置基于Web组件坐标系,对于网页缩放这种并未真正改变元素的size的行为,只会有position的改变,宽高仍保持初始值。

需要位置信息的组件如TextInput、TextArea等需将同层元素更新上报来的位置信息实时更新到组件侧。

  • 应用侧完整示例。
...
class MyNodeController extends NodeController {
  ...
  makeNode(uiContext: UIContext): FrameNode | null{

    if (this.type_ === 'application/view') {
      this.rootNode.build(wrapBuilder(TextInputBuilder), {
        textOne: "myInput",
        width: this.width_,
        height: this.height_
      }); 
    } else {
      // other
    }
    ...
  }
  ...
}



@Component
struct TextInputComponent {
  @Prop params: Params
  @State bkColor: Color = Color.Red
  mXComponentController: XComponentController = new XComponentController();

  build() {
    Column() {
      TextInput({ text: `${this.params.textOne}` })
        .height(50)
        .width(200)
        .backgroundColor(Color.Green)
        .onTouch((event) => {
          console.log('input1 event ' + JSON.stringify(event));
        }).margin({ top: 30})

      TextInput({ text: `${this.params.textOne}` })
        .height(50)
        .width(200)
        .backgroundColor(Color.Green)
        .onTouch((event) => {
          console.log('input2 event ' + JSON.stringify(event));
        }).margin({ top: 30})

      TextInput({ text: `${this.params.textOne}` })
        .height(50)
        .width(200)
        .backgroundColor(Color.Green)
        .onTouch((event) => {
          console.log('input2 event ' + JSON.stringify(event));
        }).margin({ top: 30})
    }
    .width(this.params.width)
    .height(this.params.height)
  }
}

@Builder
function TextInputBuilder(params: Params) {
  TextInputComponent({ params: params })
    .height(params.height)
    .width(params.width)
    .backgroundColor(Color.Red)
}

@Entry
@Component
struct Page {
  browserTabController: WebviewController = new webview.WebviewController()
  private nodeControllerMap: Map = new Map();
  @State componentIdArr: Array = [];
  @State edges: Edges = {};

  build() {
    Row() {
      Column() {
        Stack(){
          ForEach(this.componentIdArr, (componentId: string) => {
            NodeContainer(this.nodeControllerMap.get(componentId)).position(this.edges)
          }, (embedId: string) => embedId)

          Web({ src: $rawfile('test.html'), controller: this.browserTabController})
            .enableNativeEmbedMode(true)
            .registerNativeEmbedRule("object", "APPlication/view")
            .onNativeEmbedLifecycleChange((embed) => {
              const componentId = embed.info?.id?.toString() as string;
              if (embed.status == NativeEmbedStatus.CREATE) {
                // 建议用edges的方式使用position,避免px和vp的转换出现浮点数运算带来额外的精度损失
                this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
                let nodeController = new MyNodeController()
                nodeController.setRenderOption({surfaceId : embed.surfaceId as string,
                  type : embed.info?.type as string,
                  renderType : NodeRenderType.RENDER_TYPE_TEXTURE,
                  embedId : embed.embedId as string,
                  width : px2vp(embed.info?.width),
                  height :px2vp(embed.info?.height)})
                nodeController.rebuild()

                this.nodeControllerMap.set(componentId, nodeController)
                this.componentIdArr.push(componentId)
              } else if (embed.status == NativeEmbedStatus.UPDATE) {
                console.log("NativeEmbed update" + JSON.stringify(embed.info))

                this.edges = {left: `${embed.info?.position?.x as number}px`, top: `${embed.info?.position?.y as number}px`}
                let nodeController = this.nodeControllerMap.get(componentId)

                nodeController?.updateNode({text: 'update',   width : px2vp(embed.info?.width),
                  height :px2vp(embed.info?.height)} as ESObject)
                nodeController?.rebuild()
              } else {
                let nodeController = this.nodeControllerMap.get(componentId)
                nodeController?.setBuilderNode(null)
                nodeController?.rebuild()
              }
            })
            .onNativeEmbedGestureEvent((touch) => {
              this.componentIdArr.forEach((componentId: string) => {
                let nodeController = this.nodeControllerMap.get(componentId)
                if (nodeController?.getEmbedId() === touch.embedId) {
                  let ret = nodeController?.postEvent(touch.touchEvent)
                  if (ret) {
                    console.log("onNativeEmbedGestureEvent success " + componentId)
                  } else {
                    console.log("onNativeEmbedGestureEvent fail " + componentId)
                  }
                }
              })
            })
        }
      }
      .width('100%')
    }
    .height('100%')
  }
}
  • 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
  • 132
  • 133
  • 134
  • 135
  • 136
  • 前述应用侧相对应的前端示例。



    同层渲染测试html
    
    



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

鸿蒙全栈开发全新学习指南

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以要有一份实用的鸿蒙(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?,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:gitee.com/MNxiaona/733GH

鸿蒙开发学习资料领取!!!
微信名片
注:本文转载自blog.csdn.net的OpenHarmony_小贾的文章"https://blog.csdn.net/maniuT/article/details/140621096"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top