HarmonyOS NEXT应用开发之ArkWeb同层渲染

news/2024/7/21 10:01:11 标签: harmonyos, 华为, 移动开发, 鸿蒙开发, ArkWeb

介绍

该方案展示了ArkWeb同层渲染:将系统原生组件直接渲染到前端H5页面上,原生组件不仅可以提供H5组件无法实现的一些功能,还能提升用户体验的流畅度

效果图预览

使用说明

  1. 进入页面即可看到同层渲染效果,Text,search,image都是原生组件。

实现思路

  1. 添加权限。
"ohos.permission.INTERNET"
  1. 创建控制器管理绑定的NodeContainer组件。
class SearchNodeController extends NodeController {
  private rootNode: BuilderNode<[Params]> | undefined | null = null;
  private embedId : string = "";
  private surfaceId : string = "";
  private renderType :NodeRenderType = NodeRenderType.RENDER_componentTypeDISPLAY;
  private componentWidth : number = 0;
  private componentHeight : number = 0;
  private componentType : string = "";

  setRenderOption(params : NodeControllerParams): void {
    this.surfaceId = params.surfaceId;
    this.renderType = params.renderType;
    this.embedId = params.embedId;
    this.componentWidth = params.width;
    this.componentHeight = params.height;
    this.componentType = params.type;
  }
  /**
   * 在对应NodeContainer创建的时候调用、或者通过rebuild方法调用刷新
   */
  makeNode(uiContext: UIContext): FrameNode | null{
    this.rootNode = new BuilderNode(uiContext, { surfaceId: this.surfaceId, type: this.renderType});
    if (this.componentType === 'native/component') {
      this.rootNode.build(wrapBuilder(searchBuilder), { width : this.componentWidth, height : this.componentHeight});
    } else {
    }
    // 返回FrameNode节点
    return this.rootNode.getFrameNode();
  }
  /**
   * 设置BuilderNode节点
   */
  setBuilderNode(rootNode: BuilderNode<Params[]> | null): void{
    this.rootNode = rootNode;
  }
  /**
   * 获取BuilderNode节点
   */
  getBuilderNode(): BuilderNode<[Params]> | undefined | null{
    return this.rootNode;
  }
  /**
   * 更新BuilderNode节点
   */
  updateNode(arg: Object): void {
    this.rootNode?.update(arg);
  }
  /**
   * 获取EmbedId
   */
  getEmbedId() : string {
    return this.embedId;
  }
  /**
   * 将触摸事件派发到rootNode创建出的FrameNode上
   */
  postEvent(event: TouchEvent | undefined) : boolean {
    return this.rootNode?.postTouchEvent(event) as boolean;
  }
}
  1. 添加同层渲染的组件。
@Component
struct SearchComponent {
@Prop params: Params;
controller: SearchController = new SearchController();

build() {
  Column() {
    Column({ space: MARGIN_VERTICAL }) {
      Text($r('app.string.headline')).fontSize($r('app.string.ohos_id_text_size_body1'))
      Text($r('app.string.illustrate')).fontSize($r('app.string.ohos_id_text_size_body1'))
    }
    // 原生Text组件
    Text($r('app.string.mall')).fontSize($r('app.string.ohos_id_text_size_body1'))
    // 原生Search组件
    Search({ placeholder: 'Type to search...', controller: this.controller })
      .searchButton(SEARCH_BUTTON)
    // 原生Grid组件,Grid中包含Image和Text
    Grid() {
      // 性能知识点:此处数据量确定且数量较少,使用了ForEach,在数据量多的情况下,推荐使用LazyForeEach
      ForEach(PRODUCT_DATA, (item: ProductDataModel, index: number) => {
        GridItem() {
          Column({ space: MARGIN_VERTICAL }) {
            Image(item.uri).width($r('app.integer.image_size'))
            Row({ space: MARGIN_VERTICAL }) {
              Text(item.title).fontSize($r('app.string.ohos_id_text_size_body3'))
              Text(item.price).fontSize($r('app.string.ohos_id_text_size_body3'))
            }
          }
        }
      })
    }
    .columnsTemplate('1fr 1fr') // 2列
    .rowsTemplate('1fr 1fr ') // 2行
    .rowsGap($r('app.string.ohos_id_elements_margin_vertical_m')) // 行间距
    .columnsGap($r('app.string.ohos_id_elements_margin_vertical_m')) // 列间距
   }
 }
}
  1. embed标签可以在H5页面中嵌入任何类型的内容,在H5界面上通过embed标签标识同层元素,应用侧会将原生组件渲染到H5页面embed标签所在位置。
<div>
    <div id="bodyId">
        <!-- 在H5界面上通过embed标签标识同层元素,在应用侧将原生组件渲染到H5页面embed标签所在位置-->
        <embed id="nativeSearch" type = "native/component" width="100%" height="100%" src="view"/>
    </div>
</div>
  1. 通过WebView的enableNativeEmbedMode()控制同层渲染开关,通过onNativeEmbedLifecycleChange获取embed标签的生命周期变化数据。
build(){
  Column() {
    Stack() {
      // 性能知识点:此处componentId项确定且数量较少,使用了ForEach,在数据量多的情况下,推荐使用LazyForeEach
      ForEach(this.componentIdArr, (componentId: string) => {
        NodeContainer(this.nodeControllerMap.get(componentId));
      }, (embedId: string) => embedId)
      // web组件加载本地test.html页面
      Web({ src: $rawfile("view.html"), controller: this.browserTabController })
        .backgroundColor($r('app.color.ohos_id_color_sub_background'))
        // 不允许执行缩放
        .zoomAccess(false)
        // Todo: 知识点:通过enableNativeEmbedMode()配置同层渲染开关
        .enableNativeEmbedMode(true)
        // Todo: 知识点:通过onNativeEmbedLifecycleChange获取embed标签的生命周期变化数据
        .onNativeEmbedLifecycleChange((embed) => {
          // 获取web侧embed元素的id
          const componentId = embed.info?.id?.toString() as string
          if (embed.status === NativeEmbedStatus.CREATE) {
            // 创建节点控制器,设置参数并rebuild
            let nodeController = new SearchNodeController();
            // 外接纹理与WebView同层渲染
            nodeController.setRenderOption({surfaceId : embed.surfaceId as string, type : embed.info?.type as string, renderType : NodeRenderType.RENDER_componentTypeTEXTURE, embedId : embed.embedId as string, width : px2vp(embed.info?.width), height : px2vp(embed.info?.height)});
            nodeController.rebuild();
            // 根据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({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) {
              nodeController?.postEvent(touch.touchEvent);
            }
          })
        })
    }
  }
}
  1. h5侧通过id名获取embed标签信息,并通过embed标签添加同层渲染界面的touch监听事件;应用侧添加onNativeEmbedGestureEvent回调使得手指触摸到embed标签时能获取到触摸事件信息。
let nativeEmbed = {
  // 通过id名获取embed标签
  nativeSearch : document.getElementById('nativeSearch'),
  // 事件
  events:{},
  // 初始化
  init:function(){
    let self = this;
    // 添加touch的监听事件
    self.nativeSearch.addEventListener('touchstart', self.events, false);
  }
};
nativeEmbed.init();

Web({ src: $rawfile("view.html"), controller: this.browserTabController })
  // 获取同层渲染组件触摸事件信息
  .onNativeEmbedGestureEvent((touch) => {
    this.componentIdArr.forEach((componentId: string) => {
      let nodeController = this.nodeControllerMap.get(componentId);
      if (nodeController?.getEmbedId() === touch.embedId) {
        nodeController?.postEvent(touch.touchEvent);
      }
    })
  })

高性能知识点

ArkWeb同层渲染原生组件,原生组件不仅可以提供H5组件无法实现的一些功能,还能提升用户体验的流畅度;同层渲染节点上下树,实现节点复用,节省节点重复开销。

工程结构&模块类型

nativeembed                            // har类型
|---mock
|   |---GoodsMock.ets                  // 数据源
|---model
|   |---GoodsModel.ets                 // 数据类
|---view
|   |---NativeEmbedView.ets            // 视图层

模块依赖

本实例依赖common模块来实现资源的调用。 依赖动态路由模块来实现页面的动态加载。

参考资料

  1. Web
  2. BuilderNode
  3. NodeController
  4. ArkWeb(方舟Web)

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

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

开发基础知识:https://qr21.cn/FV7h05

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

基于ArkTS 开发:https://qr21.cn/FV7h05

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

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.
鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向


http://www.niftyadmin.cn/n/5454223.html

相关文章

鸿蒙开发之ArkUI组件常用组件文本输入

TextInput、TextArea是输入框组件&#xff0c;通常用于响应用户的输入操作&#xff0c;比如评论区的输入、聊天框的输入、表格的输入等&#xff0c;也可以结合其它组件构建功能页面&#xff0c;例如登录注册页面。 TextInput为单行输入框、TextArea为多行输入框 TextArea 多行…

谈谈 Python 中的 McNemar 检验(二)

前一篇文章说了McNemar检验&#xff0c;除了最常用的用法是用来检验列联表的相似性之外&#xff0c;它还可以用来检验两个相关样本是否来自同一分布。这里主要说一下后面这个问题。前一个问题&#xff0c;请看如下的文献&#xff1a; 谈谈 Python 中的 McNemar 检验(一)-CSDN博…

zabbix监控vmware esxi

一、zabbix服务端配置 修改zabbix_server.conf配置文件 vim /etc/zabbix/zabbix_server.conf #######zabbix_server.conf底部第二行开始添加下面配置######## StartVMwareCollectors5 #StartVMwareCollectors - 预先启动Vmware collector收集器实例的数量。此值取决于要监控的…

【漏洞复现】万户 ezOFFICE wf_printnum.jsp SQL注入漏洞

0x01 产品简介 万户OA ezoffice是万户网络协同办公产品多年来一直将主要精力致力于中高端市场的一款OA协同办公软件产品&#xff0c;统一的基础管理平台&#xff0c;实现用户数据统一管理、权限统一分配、身份统一认证。统一规划门户网站群和协同办公平台&#xff0c;将外网信…

jsonpath和json数据(序列化eval()、反序列化loads())及断言处理(断言封装)

jsonpath&#xff1a;对json串进行搜索 安装jsonpath 安装&#xff1a;pip install jsonpath 导入&#xff1a; from jsonpath import jsonpath jsonpath能通过简单的方式就能提取给定JSON中的字段。 jsonpath官方地址&#xff1a;https://goessner.net/articles/JsonPath/ 在…

【微服务篇】深入理解微服务网关原理以及Spring Gateway

微服务网关的作用 微服务网关在微服务架构中扮演着至关重要的角色&#xff0c;它主要负责请求的路由、组成服务间的通信桥梁、聚合不同服务的数据以及提供跨服务的统一认证机制。以下是微服务网关的几个主要作用&#xff1a; 请求路由: 微服务网关充当所有入站请求的入口点&a…

【C语言】C语言基础习题详解(牛客网)二分查找逻辑

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;C语言_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.三目运算符的使用 三目运算符&#xff0c;即a>b?a:b类型的&#xff0c;很多时候适当的使用三目运算符可以使得代码更简洁有序&…

PMP考试:线上班与线下班如何选择?

PMP&#xff08;项目管理专业人士&#xff09;认证是项目管理领域备受认可的一项资格认证&#xff0c;对于想要提升项目管理能力、拓展职业道路的朋友来说&#xff0c;它无疑是一个极佳的选择。在备考PMP的过程中&#xff0c;选择一个合适的培训班是至关重要的一步。作为一名已…