本文介绍如何将 H5 容器组件接入到 HarmonyOS NEXT 客户端。您可以基于已有工程使用 ohpmrc 方式接入 H5 容器 SDK 到客户端。
前置条件
添加 H5 容器 SDK 前,请您确保已经将工程接入到 mPaaS。更多信息请参见开发指南 接入 mPaaS 能力。
完成 mppm 插件安装。详情请参考 安装 mppm 工具。
引入依赖
在项目的.ohpmrc
文件中添加如下仓库:
@mpaas:registry=https://mpaas-ohpm.oss-cn-hangzhou.aliyuncs.com/meta
添加 SDK
通过 使用 mppm 工具 安装 H5 容器 组件。
配置权限
在 module.json5
中配置所需权限。
"requestPermissions":[
{
"name" : "ohos.permission.GET_NETWORK_INFO",
},
{
"name" : "ohos.permission.INTERNET",
}
]
使用 SDK
初始化
在 mPaaS 框架初始化完成之后,初始化 HRiver
。代码如下:
HRiver.init();
使用 SDK 之前必须初始化 mPaaS 框架,设置 userId 和 appsecret,其中 appsecret 从 AppCenter 后台获取,具体查看 获取 HarmonyOS NEXT config 配置文件 。
代码示例如下:
import AbilityStage from '@ohos.app.ability.AbilityStage';
import { MPFramework } from '@mpaas/framework';
export default class ModuleEntry extends AbilityStage {
async onCreate() {
MPFramework.create(this.context);
MPFramework.instance.userId = 'MPTestCase'
MPFramework.instance.appSecret = "12a711d78980f661aca5401788fdf09a";
}
}
打开离线包
初始化 HRiver
后调用 startApp
打开离线包。
import { HRiver } from '@mpaas/hriver'
/**
* 打开离线包
* @param appId: 离线包id
* @param startParams: 启动参数,可以不传。控制TitleBar、指定页面等
*/
HRiver.startApp(appId: string, startParams?: Map<string, Object>)
代码示例如下:
import { HRiver } from '@mpaas/hriver'
// 示例1:
HRiver.startApp('20190517') // 直接启动离线包
// 示例2:
let startParams: Map<string, Object> = new Map();
startParams.set('defaultTitle', '默认标题') //加载阶段显示默认标题
HRiver.startApp('20190517', startParams)
打开在线页面
初始化 HRiver
之后调用 startUrl
打开在线页面。
import { HRiver } from '@mpaas/hriver'
/**
* 打开在线页面
* @param url: 在线地址
* @param startParams: 启动参数。可以不传,控制TitleBar、指定页面等
*/
HRiver.startUrl(url: string, startParams: Map<string, Object>)
注册自定义 JSAPI
初始化 HRiver
之后调用 registerPlugin
注册自定义 JSAPI。
import { HRiver } from '@mpaas/hriver'
/**
* 注册自定义 JSApi 实现
* @param pluginClass: pluginClass列表,如 registerPlugin({CustomPlugin1, CustomPlugin2}, HRiver.SCOPE_PAGE)
* @param scope: 可以不传。默认HRiver.SCOPE_PAGE。HRiver.SCOPE_APP表示离线包App级别生命周期;HRiver.SCOPE_PAGE表示页面级别生命周期
*/
HRiver.registerPlugin(pluginClass: ESObject, scope: string)
代码示例:
HRiver.registerPlugin({H5CustomPlugin, H5Custom1Plugin})
plugin 实现代码示例如下:
import { HRiver, H5SimplePlugin, H5EventFilter, H5Event, H5BridgeContext } from '@mpaas/hriver'
class H5CustomPlugin extends H5SimplePlugin {
onPrepare(filter: H5EventFilter): void {
filter.addAction('myapi1')
}
handleEvent(event: H5Event, context: H5BridgeContext): Boolean {
if ('myapi1' == event.action) {
context.sendBridgeResult({
success: true,
data: 'myapi1调用成功'
})
return true
}
return super.handleEvent(event, context);
}
}
class H5Custom1Plugin extends H5SimplePlugin {
onPrepare(filter: H5EventFilter): void {
filter.addAction('myapi2')
}
handleEvent(event: H5Event, context: H5BridgeContext): Boolean {
if ('myapi2' == event.action) {
context.sendBridgeResult({
success: true,
data: 'myapi2调用成功'
})
return true
}
return super.handleEvent(event, context);
}
}
Native 调用 H5
Native 调用 H5 有以下两种方法。
在自定义 JSAPI 中通过
H5BridgeContext.sendToWeb
方法调用 H5。import { H5BridgeContext, H5Event, H5EventFilter, H5SimplePlugin, HRiver } from '@mpaas/hriver'; class H5CustomPlugin extends H5SimplePlugin { handleEvent(event: H5Event, context: H5BridgeContext): Boolean { // native 调用 h5 context.sendToWeb('customCallWeb', { data: 'abc' }) ... // 其他代码 } }
在 Native 代码中获取
TopApp
的activityPage
,获得最新的页面,通过页面调用sendToWeb
方法。import { HRiver, XRiverProxy, getProxy, AppManager, AppNode, Page } from '@mpaas/hriver'; { let appManager = getProxy(XRiverProxy.AppManager) as AppManager let appNode: AppNode | null = appManager.findTopApp() if (appNode != null) { let page: Page | null = appNode.getActivePage() if (page != null) { page.sendToWeb('testAction', {data: ''}) } } }
加载内置离线包
加载所有内置离线包,包括内置的公共离线包。
初始化 HRiver
之后调用 loadOfflineResource
加载内置离线包。
import { HRiver } from '@mpaas/hriver'
/**
* 加载内置离线包
* @param jsonFileName: 内置离线包的 h5_json.json 的文件名,放到rawfile目录中。如:h5_json.json。
* @param callback: 格式 (result: string) => {}。内置离线包加载完成回调的 Function
*/
HRiver.loadOfflineResource(jsonFileName: string, callback: Function)
代码示例如下:
在
entry/src/main/resources/rawfile
下添加h5_json.json
(文件从 Appcenter 后台下载即可)。{ "config":{ "updateReqRate":16400, "limitReqRate":13600, "appPoolLimit":3, "versionRefreshRate":86400 }, "data":[ { "app_desc":"离线包1", "app_id":"20180910", "auto_install":1, "fallback_base_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20180910/1.0.0.3_all/nebula/fallback/", "global_pack_url":"", "icon_url":"", "installType":1, "main_url":"/www/index.html", "name":"离线包1", "online":1, "package_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20180910/1.0.0.3_all/nebula/20180910_1.0.0.3.amr", "patch":"", "sub_url":"", "version":"1.0.0.3", "vhost":"https://20180910.h5app.com" }, { "app_desc":"离线包2", "app_id":"20190517", "auto_install":1, "fallback_base_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20190517/1.0.0.0_all/nebula/fallback/", "global_pack_url":"", "icon_url":"", "installType":1, "main_url":"/www/index.html", "name":"离线包2", "online":1, "package_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20190517/1.0.0.0_all/nebula/20190517_1.0.0.0.amr", "patch":"", "sub_url":"", "version":"1.0.0.0", "vhost":"https://20190517.h5app.com" } ], "resultCode":100, "resultMsg":"操作成功", "state":"success" }
下载 amr 并命名为
${appid}_${version}.amr
。调用 API。
import { HRiver } from '@mpaas/hriver' HRiver.loadOfflineResource('h5_json.json', (result: string) => { this.resultText = this.resultText + `\n${result} 预加载成功` })
更新离线包
import { HRiver } from '@mpaas/hriver'
/**
* 批量更新离线包
* @param appIds: 需要更新离线包的appId列表
* @param updateCallback: 格式必须 (result: boolean, code: number) => {}。更新接口返回后回调的Function
*/
HRiver.updateApp(appIds?: Array<string>, updateCallback?: Function)
/**
* 更新所有离线包。
* @param updateCallback: 格式必须 (result: boolean, code: number) => {}。更新接口返回后回调的Function
*/
HRiver.updateAll(updateCallback: Function)
/**
* 批量更新离线包
* @param appIds: 离线包的appId和version的map
* @param updateCallback: 格式必须 (result: boolean, code: number) => {}。更新接口返回后回调的Function
*/
HRiver.updateAppWithVersion(appIds?: Map<string, string>, updateCallback?: Function)
代码示例如下:
import { HRiver } from '@mpaas/hriver'
HRiver.updateApp(['90000002'], (result: boolean, code: number) => {
this.resultText = `90000002更新结果: ${result}`
})
配置公共离线包
设置
H5CommonAppProvider
。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * 配置公共离线包 Provider,必须在 HRiver.init() 之前调用 */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())
说明setProvider
必须在HRiver.init()
之前调用。实现
H5AppCommonProviderImpl.ets
的getCommonResourceAppList
方法。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { getCommonResourceAppList(): Array<string> { return ['20220719'] // 返回公共离线包id列表 } }
支持 fallback 逻辑
保持和 iOS/Android 一致,离线包没下载成功情况下优先打开 fallback 在线地址。
该功能默认关闭,可以通过以下开关打开:
实现 H5CommonAppProvider 的 configJSON 方法,增加 enableFallback 参数。
export class H5AppCommonProviderImpl extends H5CommonAppProvider {
... // 其他配置
// configJson,配置更新频率等
configJSON(): string {
return JSON.stringify({
enableFallback: 'YES',
xxx // 其他配置
})
}
}
设置 UserAgent
import { HRiver } from '@mpaas/hriver'
HRiver.setUserAgent(userAgent)
setUserAgent
在 HRiver.init()
之后调用,会在默认 UserAgent
之后拼接设置的 useragent
。
设置离线包默认更新频率
设置
H5CommonAppProvider
。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * 配置公共离线包Provider、离线包更新频率等功能,必须在HRiver.init()之前调用 */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())
说明setProvider
必须在HRiver.init()
之前调用。实现
H5CommonAppProvider
的configJSON
方法,代码示例如下。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { ... // 其他配置 // configJson,配置更新频率等 configJSON(): string { return JSON.stringify({ h5_nbmngconfig: "{\"config\":{\"al\":\"3\",\"pr\":{\"4\":\"86400\",\"common\":\"864000\"},\"ur\":\"1\",\"fpr\":{\"common\":\"3888000\"}},\"switch\":\"yes\"}" }) } }
具体参数如下:
h5_nbmngconfig: "{\"config\":{\"al\":\"3\",\"pr\":{\"4\":\"86400\",\"common\":\"864000\"},\"ur\":\"1800\",\"fpr\":{\"common\":\"3888000\"}},\"switch\":\"yes\"}"
其中的
ur: 1800
表示更新频率为 1800 秒,使用时修改ur
的值即可。
配置离线包签名校验
设置
H5CommonAppProvider
。import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * 配置公共离线包Provider、离线包更新频率、签名校验等功能,必须在HRiver.init()之前调用 */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())
说明setProvider
必须在HRiver.init()
之前调用。实现
H5CommonAppProvider
的shouldVerify
和pubKey
方法,代码示例如下:import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { ... // 其他配置 /** * 是否开启离线包校验,默认关闭 * return: true表示开启,false表示关闭 */ shouldVerify(): boolean { return false } // 离线包校验的公钥,如果shouldVerify为false 则无需设置,否则必须设置公钥 pubKey(): string { return '' } }
开启调试模式
HRiver.enableDebug(true)
监听页面生命周期
初始化完成后设置 provider。
import {H5PageLifeCycleProvider} from '@mpaas/hriver';
HRiver.setProvider(H5PageLifeCycleProvider.name, new H5PageLifeCycle())
H5PageLifeCycle
import { H5PageLifeCycleProvider, Page } from '@mpaas/hriver';
import { hilog } from '@kit.PerformanceAnalysisKit';
export class H5PageLifeCycle extends H5PageLifeCycleProvider {
onPageShow(routerName: string, page?: Page | undefined): void {
super.onPageShow(routerName, page);
hilog.debug(1, 'H5PageLifeCycle', "pageshow: " + page?.pageUrl)
}
onPageHide(routerName: string, page?: Page | undefined): void {
super.onPageHide(routerName, page);
hilog.debug(1, 'H5PageLifeCycle', "onPageHide: " + page?.pageUrl)
}
onPageCreate(page?: Page | undefined): void {
super.onPageCreate(page);
hilog.debug(1, 'H5PageLifeCycle', "onPageCreate: " + page?.pageUrl)
}
onPageExit(page?: Page | undefined): void {
super.onPageExit(page);
hilog.debug(1, 'H5PageLifeCycle', "onPageExit: " + page?.pageUrl)
}
onBackPress(page?: Page | undefined): boolean {
hilog.debug(1, 'H5PageLifeCycle', "onBackPress: " + page?.pageUrl)
return super.onBackPress(page);
}
}
支持注销 Plugin
通过 Page 的 Page.ets 实现注销。
// 根据action注销
unregisterPluginByAction(action: string);
// 根据pluginName注销
unregisterPluginByPluginName(name: string);
示例如下:
支持页面嵌入模式
页面嵌入模式基于 Navigation 实现。
创建离线包需要的 NavPathStack。
pageInfos: NavPathStack = new NavPathStack()
在页面需要嵌入的位置添加空白组件,以下为示例:
重要mode 必须使用
NavigationMode.Stack
,否则横竖屏/折叠屏切换有问题。Navigation(this.pageInfos) { Column() { // 空白页面用于嵌入离线包页面, 不用填任何内容 }.width('100%') // 宽度根据需要 .backgroundColor(Color.Red) // 只是示例 .height(500) // 高度根据实际 }.navDestination(this.PagesMap) .mode(NavigationMode.Stack) // pagesMap实现: import {HRBuilder, RouterUtils, HRiver, H5RouterNavStackProvider} from '@mpaas/hriver' let mPaaSHRiverBuilder: WrappedBuilder<[string, ESObject]> = wrapBuilder(HRBuilder); @Builder PagesMap(name: string, params: ESObject) { if (RouterUtils.isMPHRiverPage(name)) { mPaaSHRiverBuilder.builder(name, params) } else if (name == 'xxx') { // 其他业务的页面 } }
启动离线包和在线页面时,需要添加第三个参数并传入步骤 1 中创建的 NavPathStack,增加 embedPage 参数用来表示内嵌页面。
let param: Map<string, Object> = new Map<string, Object>() param.set('embedPage', 'YES') HRiver.startUrl('https://www.baidu.com', param, this.pageInfos) // HRiver.startApp('90000000', param, this.pageInfos)
添加返回事件拦截。
HRiver.setProvider(H5PageLifeCycleProvider.name, new H5PageLifeCycle())
import { H5PageLifeCycleProvider, Page } from '@mpaas/hriver'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { router } from '@kit.ArkUI'; export class H5PageLifeCycle extends H5PageLifeCycleProvider { onPageShow(routerName: string, page?: Page | undefined): void { super.onPageShow(routerName, page); hilog.debug(1, 'H5PageLifeCycle', "onPageShow: " + page?.pageUrl) } onPageHide(routerName: string, page?: Page | undefined): void { super.onPageHide(routerName, page); hilog.debug(1, 'H5PageLifeCycle', "onPageHide: " + page?.pageUrl) } onPageCreate(page?: Page | undefined): void { super.onPageCreate(page); hilog.debug(1, 'H5PageLifeCycle', "onPageCreate: " + page?.pageUrl) } onPageExit(page?: Page | undefined): void { super.onPageExit(page); hilog.debug(1, 'H5PageLifeCycle', "onPageExit: " + page?.pageUrl) } onBackPress(page?: Page | undefined): boolean { hilog.debug(1, 'H5PageLifeCycle', "onBackPress: " + page?.pageUrl) let navPathStack: NavPathStack|undefined = page?.getSession()?.getRouter()?.getNavPathStack() if (navPathStack && page && page.embedPage && navPathStack.size() == 1) { router.back() return true; } return super.onBackPress(page); } }
UI 定制
自定义导航栏
初始化完成后通过
provider
设置自定义导航栏。import {HRBuilder, RouterUtils, HRiver, H5CommonAppProvider, H5RouterNavStackProvider, CustomUIBuilderProvider, H5CacheProvider } from '@mpaas/hriver' HRiver.setProvider(CustomUIBuilderProvider.name, new CustomUIBuilderProviderImpl())
实现
CustomUIBuilderProviderImpl
。import { CustomUIBuilderProvider, Page } from '@mpaas/hriver'; import { CustomUIBuilder } from '../pages/CustomTitleBarComponent'; export class CustomUIBuilderProviderImpl extends CustomUIBuilderProvider { getCustomUIBuilder(): WrappedBuilder<[string, Page]> { return wrapBuilder(CustomUIBuilder); } }
其中
CustomUIBuilder
为业务自定义titlebar
组件的全局 Builder。实现参考如下:/** * name: 自定义组件的名称 * page: 自定义titlebar对应的页面Page */ @Builder export function CustomUIBuilder(name: string, p: Page) { if (name === 'titleBar') { CustomTitleBarComponent({page: p, titleBarData: p.titleBarData}) } }
CustomTitleBarComponent
为具体的标题组件,titleBarData
为标题栏所需要的数据,数据发生变化会实时刷新。示例参考如下:import { H5NavMenuItemData, HRiverUtil, Page, TitleBarData } from '@mpaas/hriver' const TAG: string = "CustomTitleBarComponent" const MENU_MARGIN: number = 5 const DEFAULT_MARGIN: number = 12 @Builder export function CustomUIBuilder(name: string, p: Page) { if (name === 'titleBar') { CustomTitleBarComponent({page: p, titleBarData: p.titleBarData}) } } @Component export struct CustomTitleBarComponent { page: Page | null = null @Prop titleBarData: TitleBarData aboutToAppear(): void { } build() { RelativeContainer() { Button() { Image(this.getBackIconImage()) .id('titlebar_back_img') .width(12) .height(20) } .width(48) .height('100%') .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top } }) .id("h5_nav_close") .onClick((event) => { if (this.page != null) { this.page.backClickEvent() } }) Flex({ justifyContent: FlexAlign.Center, direction: FlexDirection.Column }) { //如果设置了图片标题就只显示图片 if (this.titleBarData.titleImage) { Image(this.titleBarData.titleImage) .id('titlebar_title_image') .height(36).onClick(() => { this.onTitleClick() }) } else { Flex({ justifyContent: FlexAlign.Start,direction: FlexDirection.Row }){ Text(this.titleBarData.title) .fontSize(18) .textAlign(TextAlign.Start) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .fontColor(this.titleBarData.titleColor) .onClick(() => { this.onTitleClick() }) if(this.titleBarData.showTitleLoading ){ Image(this.getTitleBarLoadingIcon()).width(18).height(18) .id('titlebar_progress_img') .margin({left:5,top:1}) .rotate({ angle: this.titleBarData.loadingRotateAngle }) .animation({ duration:3000, curve: Curve.Linear, delay: 0, iterations: -1, playMode: PlayMode.Normal, }).onAppear(()=>{ this.titleBarData.loadingRotateAngle = 360 }) } } if (this.titleBarData.subtitle) { Text(this.titleBarData.subtitle) .textAlign(TextAlign.Start) .fontColor(this.titleBarData.titleColor) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .fontSize(18) .onClick(() => { this.onTitleSubtitleClick() }) } } }.id("h5_tv_title") .height("100%") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, left: { anchor: 'h5_nav_close', align: HorizontalAlign.End }, right: { anchor: "h5_nav_options", align: HorizontalAlign.Start } }) if (this.titleBarData.optionMenuState) { if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_TITLE) { Text(this.titleBarData.menuTitle) .fontSize(16) .align(Alignment.Center) .textAlign(TextAlign.Center) .fontColor(this.titleBarData.menuColor) .height('100%') .id("h5_nav_options") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }) .onClick(() => { this.onMoreClick(false, 0) }) .margin({ right: DEFAULT_MARGIN }) } else if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_ICON) { Button() { Image(HRiverUtil.getIconImage(this.titleBarData.menuIcon)) .id('titlebar_right_icon') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(30) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options") .height("100%") .onClick(() => { this.onMoreClick(false, 0) }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }) .margin({ right: DEFAULT_MARGIN }) } else if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_MORE) { Button() { Image(HRiverUtil.getIconImage('more')) .id('titlebar_right_more') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(48) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options") .height("100%") .margin({ right: DEFAULT_MARGIN }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }).onClick(() => { if (!this.titleBarData.preventDefault) { this.titleBarData.customPopup = !this.titleBarData.customPopup } this.onMoreClick(true, 0) }) .bindPopup(this.titleBarData.customPopup , { builder: this.MenuBuilder, placement:Placement.BottomLeft, popupColor:"#fff", onStateChange: (e) => { console.info(JSON.stringify(e.isVisible)) if (!e.isVisible) { this.titleBarData.customPopup = false } } }) } if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_TITLE) { Text(this.titleBarData.menuTitle1) .fontSize(16) .align(Alignment.Center) .textAlign(TextAlign.Center) .fontColor(this.titleBarData.menuColor1) .height('100%') .id("h5_nav_options1") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }) .margin({ right: MENU_MARGIN }) .onClick(()=> { this.onMoreClick(false, 1) }) } else if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_ICON) { Button() { Image(HRiverUtil.getIconImage(this.titleBarData.menuIcon1)) .id('titlebar_right_icon1') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(30) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options1") .height("100%") .onClick(()=> { this.onMoreClick(false, 1) }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }) .margin({ right: MENU_MARGIN }) } else if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_MORE) { Button() { Image(HRiverUtil.getIconImage('more')) .id('titlebar_right_more1') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(48) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options1") .height("100%") .margin({ right: MENU_MARGIN }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }).onClick(() => { if (!this.titleBarData.preventDefault) { this.titleBarData.customPopup1 = !this.titleBarData.customPopup1 } this.onMoreClick(true, 1) }) .bindPopup(this.titleBarData.customPopup1 , { builder: this.MenuBuilder, placement:Placement.BottomLeft, popupColor:"#fff", onStateChange: (e) => { console.info(JSON.stringify(e.isVisible)) if (!e.isVisible) { this.titleBarData.customPopup1 = false } } }) } } }.height(this.titleBarData.showTitleBar ? 48 : 0) } getTitleBarLoadingIcon(): Resource | string { return $rawfile(`icon/h5_title_bar_progress_bg.webp`) } getIconImage(icon: string): Resource | string { return HRiverUtil.getIconImage(icon) } getBackIconImage(): Resource | string { return $rawfile(`icon/hriverback.webp`) } onTitleClick() { if (this.page != null) { this.page.titleBarClickEvent() } } onTitleSubtitleClick() { if (this.page != null) { this.page.subTitleBarClickEvent() } } onMoreClick(fromMenu:boolean, index: number) { if (this.page != null) { this.page.onMoreClick(fromMenu, index) } } onMoreItemClick(tag: string, name: string,isShowPopMenu:boolean) { if (this.page != null) { this.page.onMoreItemClick(tag, name, isShowPopMenu) } } @Builder MenuBuilder() { Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { ForEach(this.titleBarData.h5NavMenuItemList, (item: H5NavMenuItemData, index) => { Column() { Row() { Image(item.icon).width(20).height(20).margin({ right: 5 }) Text(item.name).fontSize(16) } .width('100%') .height(30) .padding({ left: 10 }) .justifyContent(FlexAlign.Start) .align(Alignment.Center) .onClick(() => { if (item) { this.onMoreItemClick(item.tag || '', item.name || '',false) this.titleBarData.customPopup = false this.titleBarData.customPopup1 = false } }) if (index != this.titleBarData.h5NavMenuItemList.length - 1) { Divider().height(10).width('90%').color('#ccc') } }.padding(5).height(40) }) }.width(150).height(165).backgroundColor("#fff") } }
自定义离线包加载页/错误页
初始化完成之后设置 Provider。
import { CustomLoadingBuilderProvider} from '@mpaas/hriver'; HRiver.setProvider(CustomLoadingBuilderProvider.name, new CustomLoadingBuilderProviderImpl())
实现
CustomLoadingBuilderProviderImpl
类。import { CustomLoadingBuilderProvider, H5Router, HRLoadingData } from '@mpaas/hriver'; import { CustomUIBuilder } from './CustomLoadingComponent'; export class CustomLoadingBuilderProviderImpl extends CustomLoadingBuilderProvider { getCustomUIBuilder(): WrappedBuilder<[string, HRLoadingData, H5Router]> { return wrapBuilder(CustomUIBuilder); } }
实现
CustomLoadingComponent.ets
类,其中通过loadingStatus
控制加载状态。import { H5Router, HRLoadingData, LoadingStatus } from '@mpaas/hriver' const TAG: string = "CustomLoadingComponent" export const Loading_STATE_Init = 0 export const Loading_STATE_Start = 1 export const Loading_STATE_End = 2 export const Loading_STATE_Err = 3 @Builder export function CustomUIBuilder(name: string, loadingData: HRLoadingData, h5Router: H5Router) { if (name === 'loading') { CustomLoadingComponent({loadingStatus: loadingData.loadingStatus, h5Router: h5Router}) } } @Component export struct CustomLoadingComponent { @ObjectLink loadingStatus: LoadingStatus h5Router?: H5Router aboutToAppear(): void { } build() { Row() { Column() { Flex({ direction: FlexDirection.Row }) { //返回按钮 Button() { Image($rawfile("icon/hriverback.webp")) .width(12) .height(20) } .width(48) .height('100%') .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .onClick((event) => { this.h5Router?.routerBack() }) }.width('100%') .height(48) Image(this.loadingStatus.icon ? this.loadingStatus.icon : $rawfile("icon/hriverloading.webp")) .width(40) .height(40) .margin({top: 38}) if (this.loadingStatus.state == Loading_STATE_Err || this.loadingStatus.state == Loading_STATE_Start){ Text(this.loadingStatus.state == Loading_STATE_Err ? `网络不给力,请稍后再试 \n(${this.loadingStatus.code} ${this.loadingStatus.msg})` : this.loadingStatus.title) .fontSize(18) .margin({top: 15}) .textAlign(TextAlign.Center) } } .width('100%') .margin({ top: 48 }) } } }
自定义在线 URL 加载失败的错误页
设置 Provider 拦截网页错误回调。
HRiver.setProvider(H5WebClientProvider.name, new H5WebClientProviderImpl());
在
H5WebClientProviderImpl
中实现onErrorReceive
方法。import { MPFramework } from '@mpaas/framework'; import { H5WebClientProvider, Page } from '@mpaas/hriver'; import { util } from '@kit.ArkTS'; export class H5WebClientProviderImpl extends H5WebClientProvider{ onErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, err: WebResourceError | undefined): boolean { let errorUrl = request?.getRequestUrl() let errorCode = err?.getErrorCode() if (errorCode == 403 || errorCode == 404) { // keep same with ios,not show errorPage for 404 and 403 // log(TAG, "ignoreErrorPage 404 or 403, return "); return true; } let lastUrl = page?.webcontroller?.getUrl() if (errorUrl == page?.pageUrl || errorUrl == `${page?.pageUrl}/`) { // 从rawfile中读取自定义错误页面demo_custom_err.html let dataBytes = MPFramework.instance.context.resourceManager.getRawFileContentSync('demo_custom_err.html') let textDecoder = new util.TextDecoder("utf-8", { fatal: false, ignoreBOM: false }) let html = textDecoder.decodeWithStream(new Uint8Array(dataBytes), { stream: true }) page?.webcontroller?.loadData(html, "text/html", "utf-8", lastUrl) return true; } return false } }
在
demo_custom_err.html
文件中编写错误页代码。<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="apple-mobile-web-app-capable" content="yes"/> <meta name="apple-mobile-web-app-status-bar-style" content="black"/> <meta name="format-detection" content="telephone=no"/> <meta name="format-detection" content="email=no"/> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0"/> <title>!!!!</title> <style type="text/css"> body { background-color: #FFF; } .am-page-result { text-align: center; } .am-page-result .am-page-result-pic { width: 135px; height: 135px; margin: 40px auto; } .am-page-result .am-page-result-pic img { width: 100%; height: 100%; } .am-page-result p { margin: 0; font-size: 16px; color: #999; } .am-page-result-button { margin-top: 25px; display: -webkit-box; display: -webkit-flex; display: flex; } .am-button { display: block; margin: 0 10px; padding: 0 10px; height: 42px; text-align: center; font-size: 18px; line-height: 42px; border-radius: 4px; outline: 0; -webkit-appearance: none; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; width: 50%; } .am-button[am-mode~=white] { border: 1px solid #DDD; color: #666; background-color: #FFF; } .am-button[am-mode~=white]:active { border-color: #D8D8D8; background-color: #F8F8F8; } .am-button[am-mode~=blue] { border: 1px solid #28F; color: #FFF; background-color: #39F; } .am-button[am-mode~=blue]:active { border-color: #17F; background-color: #28F; } .am-button[am-mode~=light], .am-button[am-mode~=light]:active { border: none; color: #39F; background-color: #FFF; } @media screen and (min-device-width: 375px) { .am-page-result .am-page-result-pic { width: 160px; height: 160px; margin: 45px auto; } .am-page-result-button { margin-top: 30px; } .am-button { margin: 0 20px; height: 44px; line-height: 44px; } } @media screen and (min-device-width: 414px) { .am-page-result .am-page-result-pic { width: 180px; height: 180px; margin: 50px auto; } .am-button { margin: 0 24px; height: 50px; line-height: 50px; } } </style> <body> <div class="am-page-result"> <div class="am-page-result-pic"> <img src="data:image/png;base64,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"/> </div> <p>这是自定义错误页面</p> </div> </body> <script> </script> </html>
设置和原生 View 一起滚动
//在启动参数里添加
// scrollForward: 0: SELF_ONLY, 1: SELF_FIRST, 2: PARENT_FIRST, 3 : PARALLEL
// scrollBackward: 0: SELF_ONLY, 1: SELF_FIRST, 2: PARENT_FIRST, 3 : PARALLEL
param.set("scrollForward", 2)
param.set("scrollBackward", 1)
支持限制 Web 组件宽度
通过启动参数控制。
params.set('webWidth', xxx)。 // 参数支持string ('100%'百分比方式) 或者number (例如800表示800px)
动态控制。
page.setWebWidth(webWidth: string | number)
鸿蒙 Web 行为定制
H5WebClientProvider
可以通过实现 H5WebClientProvider
修改鸿蒙 H5 容器 Web 的一些默认行为 API 。
HRiver.setProvider(H5WebClientProvider.name, new H5WebClientProviderImpl())
可定制的 API 如下:
export class H5WebClientProvider {
// 页面http错误回调
onHttpErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, response: WebResourceResponse | undefined) {
}
// 页面下载回调
onDownloadStart(page: Page | undefined, url: string | undefined, userAgent: string | undefined, contentDisposition: string | undefined,
mimetype: string | undefined, contentLength: number | undefined) {
}
// 页面ssl错误回调
onSslErrorEventReceive(page: Page | undefined, handler: SslErrorHandler, error: SslError) {
}
// 页面错误回调
onErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, error: WebResourceError | undefined): boolean {
return false
}
// 全屏回调
onFullScreenEnter(page: Page | undefined, handler: FullScreenExitHandler) {
}
// 退出全屏回调
onFullScreenExit(page: Page | undefined) {
}
// web权限申请回调
onPermissionRequest(page: Page | undefined, request: PermissionRequest | undefined) {
}
// web screencapturerequest回调
onScreenCaptureRequest(page: Page | undefined, handler: ScreenCaptureHandler | undefined) {
}
onPageBegin(page: Page | undefined, url: string | undefined) {
}
onAppear(page: Page | undefined) {
}
// web scroll回调
onScroll(x: number, y: number, page: Page | undefined) {
}
getWindow(context: Context, page: Page | undefined): Promise<window.Window> | undefined {
return undefined
}
// web 长按菜单
onContextMenuShow(param: WebContextMenuParam | undefined, result: WebContextMenuResult | undefined) {
return false
}
// web 文件选择回调
onShowFileSelector(fileSelector: FileSelectorParam, result: FileSelectorResult, page: Page | undefined) {
return false
}
// web 定位相关回调
onGeolocationShow(origin: string | undefined, geoLocation: JsGeolocation | undefined, page: Page | undefined) {
}
// web 定位相关回调
onGeolocationHide(page: Page | undefined) {
}
}
H5MixModeSettingProvider
可以通过 H5MixModeSettingProvider
设置 Web 支持 HTTP/HTTPS 的 MixedMode
,具体使用如下:
HRiver.setProvider(H5MixModeSettingProvider.name, new H5MixModeSettingProviderImpl()) // 业务实现H5MixModeSettingProviderImpl
export class H5MixModeSettingProviderImpl extends H5MixModeSettingProvider {
mixMode(page: Page | undefined): MixedMode {
// 根据业务实际情况返回对应的MixedMode
return MixedMode.Compatible
}
}
鸿蒙系统不支持在 HTTPS 页面加载地址为 HTTP 的 IP 链接,要么加载 HTTPS 的 IP 的链接,要么加载 HTTP 的非 IP 链接。
页面路由支持 Navigation
默认页面路由使用 router 方式,鸿蒙 router 方式不支持关闭栈中某个页面,只能一级级回退。离线包支持 Navigation 模式:
支持关闭栈中页面
支持分栏模式
全局 Navigation 模式
HRiver 初始化完成后,在启动离线包之前,需设置
H5RouterNavStackProvider
。HRiver.setProvider(H5RouterNavStackProvider.name, new NavStackProvider(this.pageInfos))
import { H5RouterNavStackProvider } from '@mpaas/hriver'; export class NavStackProvider extends H5RouterNavStackProvider { navStack: NavPathStack // 全局的navPathStack栈 constructor(navStack: NavPathStack) { super(); this.navStack = navStack; } getNavPathStack(): NavPathStack { return this.navStack } }
在
navDestination
中配置builder
,builder
中加载 mPaaS 全局 Builder。import {HRBuilder, RouterUtils, HRiver, H5RouterNavStackProvider} from '@mpaas/hriver' let mPaaSHRiverBuilder: WrappedBuilder<[string, ESObject]> = wrapBuilder(HRBuilder); @Builder PagesMap(name: string, params: ESObject) { if (RouterUtils.isMPHRiverPage(name)) { mPaaSHRiverBuilder.builder(name, params) } else if (name == 'xxx') { // 其他业务的页面 } } build() { Navigation(this.pageInfos) { Row() { Column() { // 业务页面 MainPage() } .width('100%') } }.navDestination(this.PagesMap) }
离线包独立使用 Navigation 模式
HRiver.startApp
/HRiver.startUrl
的第三个参数传入 NavPathStack 即可。参考 全局 Navigation 模式 中的步骤 2。