HarmonyOS NEXT應用開發(fā)之ArkWeb同層渲染

介紹

該方案展示了ArkWeb同層渲染:將系統(tǒng)原生組件直接渲染到前端H5頁面上刺彩,原生組件不僅可以提供H5組件無法實現(xiàn)的一些功能缰冤,還能提升用戶體驗的流暢度

效果圖預覽

使用說明

  1. 進入頁面即可看到同層渲染效果,Text,search鲤看,image都是原生組件。

實現(xiàn)思路

  1. 添加權限。
"ohos.permission.INTERNET"
  1. 創(chuàng)建控制器管理綁定的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創(chuàng)建的時候調(diào)用巴碗、或者通過rebuild方法調(diào)用刷新
   */
  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節(jié)點
    return this.rootNode.getFrameNode();
  }
  /**
   * 設置BuilderNode節(jié)點
   */
  setBuilderNode(rootNode: BuilderNode<Params[]> | null): void{
    this.rootNode = rootNode;
  }
  /**
   * 獲取BuilderNode節(jié)點
   */
  getBuilderNode(): BuilderNode<[Params]> | undefined | null{
    return this.rootNode;
  }
  /**
   * 更新BuilderNode節(jié)點
   */
  updateNode(arg: Object): void {
    this.rootNode?.update(arg);
  }
  /**
   * 獲取EmbedId
   */
  getEmbedId() : string {
    return this.embedId;
  }
  /**
   * 將觸摸事件派發(fā)到rootNode創(chuàng)建出的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() {
      // 性能知識點:此處數(shù)據(jù)量確定且數(shù)量較少橡淆,使用了ForEach,在數(shù)據(jù)量多的情況下母赵,推薦使用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頁面中嵌入任何類型的內(nèi)容逸爵,在H5界面上通過embed標簽標識同層元素,應用側(cè)會將原生組件渲染到H5頁面embed標簽所在位置凹嘲。
<div>
    <div id="bodyId">
        <!-- 在H5界面上通過embed標簽標識同層元素痊银,在應用側(cè)將原生組件渲染到H5頁面embed標簽所在位置-->
        <embed id="nativeSearch" type = "native/component" width="100%" height="100%" src="view"/>
    </div>
</div>
  1. 通過WebView的enableNativeEmbedMode()控制同層渲染開關,通過onNativeEmbedLifecycleChange獲取embed標簽的生命周期變化數(shù)據(jù)施绎。
build(){
  Column() {
    Stack() {
      // 性能知識點:此處componentId項確定且數(shù)量較少溯革,使用了ForEach,在數(shù)據(jù)量多的情況下谷醉,推薦使用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'))
        // 不允許執(zhí)行縮放
        .zoomAccess(false)
        // Todo: 知識點:通過enableNativeEmbedMode()配置同層渲染開關
        .enableNativeEmbedMode(true)
        // Todo: 知識點:通過onNativeEmbedLifecycleChange獲取embed標簽的生命周期變化數(shù)據(jù)
        .onNativeEmbedLifecycleChange((embed) => {
          // 獲取web側(cè)embed元素的id
          const componentId = embed.info?.id?.toString() as string
          if (embed.status === NativeEmbedStatus.CREATE) {
            // 創(chuàng)建節(jié)點控制器致稀,設置參數(shù)并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();
            // 根據(jù)web傳入的embed的id屬性作為key,將nodeController存入map
            this.nodeControllerMap.set(componentId, nodeController);
            // 將web傳入的embed的id屬性存入@State狀態(tài)數(shù)組變量中俱尼,用于動態(tài)創(chuàng)建nodeContainer節(jié)點容器抖单,需要將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側(cè)通過id名獲取embed標簽信息,并通過embed標簽添加同層渲染界面的touch監(jiān)聽事件;應用側(cè)添加onNativeEmbedGestureEvent回調(diào)使得手指觸摸到embed標簽時能獲取到觸摸事件信息遇八。
let nativeEmbed = {
  // 通過id名獲取embed標簽
  nativeSearch : document.getElementById('nativeSearch'),
  // 事件
  events:{},
  // 初始化
  init:function(){
    let self = this;
    // 添加touch的監(jiān)聽事件
    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組件無法實現(xiàn)的一些功能,還能提升用戶體驗的流暢度刃永;同層渲染節(jié)點上下樹货矮,實現(xiàn)節(jié)點復用,節(jié)省節(jié)點重復開銷斯够。

工程結構&模塊類型

nativeembed                            // har類型
|---mock
|   |---GoodsMock.ets                  // 數(shù)據(jù)源
|---model
|   |---GoodsModel.ets                 // 數(shù)據(jù)類
|---view
|   |---NativeEmbedView.ets            // 視圖層

模塊依賴

本實例依賴common模塊來實現(xiàn)資源的調(diào)用囚玫。 依賴動態(tài)路由模塊來實現(xiàn)頁面的動態(tài)加載。

參考資料

  1. Web
  2. BuilderNode
  3. NodeController
  4. ArkWeb(方舟Web)
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末读规,一起剝皮案震驚了整個濱河市抓督,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌束亏,老刑警劉巖铃在,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碍遍,居然都是意外死亡定铜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門雀久,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宿稀,“玉大人,你說我怎么就攤上這事赖捌∽7校” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵越庇,是天一觀的道長罩锐。 經(jīng)常有香客問我,道長卤唉,這世上最難降的妖魔是什么涩惑? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮桑驱,結果婚禮上竭恬,老公的妹妹穿的比我還像新娘跛蛋。我一直安慰自己,他們只是感情好痊硕,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布赊级。 她就那樣靜靜地躺著,像睡著了一般岔绸。 火紅的嫁衣襯著肌膚如雪理逊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天盒揉,我揣著相機與錄音晋被,去河邊找鬼。 笑死刚盈,一個胖子當著我的面吹牛羡洛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扁掸,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼翘县,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了谴分?” 一聲冷哼從身側(cè)響起锈麸,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牺蹄,沒想到半個月后忘伞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡沙兰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年氓奈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鼎天。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡舀奶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出斋射,到底是詐尸還是另有隱情育勺,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布罗岖,位于F島的核電站涧至,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏桑包。R本人自食惡果不足惜南蓬,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赘方,春花似錦烧颖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泳梆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間榜掌,已是汗流浹背优妙。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留憎账,地道東北人套硼。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像胞皱,于是被迫代替她去往敵國和親邪意。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容